From a2e8bc2977caa0d2a7b7c686a8a8f293a9c68c01 Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 30 Dec 2025 11:14:07 -0600 Subject: [PATCH 01/19] GitHub Issue 503: Field editor URL option to set target window (i.e. _blank) --- .../components/releaseNotes/components.md | 4 ++ .../NameAndLinkingOptions.tsx | 40 ++++++++++++++----- .../components/domainproperties/constants.ts | 1 + .../components/domainproperties/models.tsx | 12 ++++++ .../src/theme/domainproperties.scss | 2 +- 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/packages/components/releaseNotes/components.md b/packages/components/releaseNotes/components.md index 65fdbe2874..6a42cae7c2 100644 --- a/packages/components/releaseNotes/components.md +++ b/packages/components/releaseNotes/components.md @@ -1,6 +1,10 @@ # @labkey/components Components, models, actions, and utility functions for LabKey applications and pages +### version TBD +*Released*: TBD +- GitHub Issue 503: Field editor URL option to set target window (i.e. _blank) + ### version 7.7.1 *Released*: 29 December 2025 - [GitHub Issue 734](https://github.com/LabKey/internal-issues/issues/734) Update sizing of comment input box for better display in narrow screens diff --git a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx index 292762ecfb..5faeea8aa8 100644 --- a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx +++ b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx @@ -16,6 +16,7 @@ import { DOMAIN_FIELD_LABEL, DOMAIN_FIELD_ONTOLOGY_PRINCIPAL_CONCEPT, DOMAIN_FIELD_URL, + DOMAIN_FIELD_URL_TARGET_WINDOW, } from './constants'; import { DomainField, IDomainFormDisplayOptions } from './models'; import { SectionHeading } from './SectionHeading'; @@ -39,6 +40,12 @@ export class NameAndLinkingOptions extends PureComponent { this.props?.onChange(id, value); }; + handleURLTargetWindowChange = (evt: any): void => { + const id = evt.target.id; + const isChecked = evt.target.checked; + this.onChange(id, isChecked ? '_blank' : null); + }; + getImportAliasHelpText = (): ReactNode => { return ( <> @@ -120,6 +127,16 @@ export class NameAndLinkingOptions extends PureComponent { )}
+ {!appPropertiesOnly && + hasModule(ONTOLOGY_MODULE_NAME) && + !field.isUniqueIdField() && + !field.isCalculatedField() && ( + + )}
@@ -132,16 +149,19 @@ export class NameAndLinkingOptions extends PureComponent { onChange={this.handleChange} disabled={isFieldFullyLocked(field.lockType)} /> - {!appPropertiesOnly && - hasModule(ONTOLOGY_MODULE_NAME) && - !field.isUniqueIdField() && - !field.isCalculatedField() && ( - - )} + {/*GitHub Issue 503: Field editor URL option to set target window (i.e. _blank)*/} +
+ + Open links in a new tab +
diff --git a/packages/components/src/internal/components/domainproperties/constants.ts b/packages/components/src/internal/components/domainproperties/constants.ts index 7525f0dce1..7d1511ebf3 100644 --- a/packages/components/src/internal/components/domainproperties/constants.ts +++ b/packages/components/src/internal/components/domainproperties/constants.ts @@ -27,6 +27,7 @@ export const DOMAIN_FIELD_DESCRIPTION = 'description'; export const DOMAIN_FIELD_LABEL = 'label'; export const DOMAIN_FIELD_IMPORTALIASES = 'importAliases'; export const DOMAIN_FIELD_URL = 'URL'; +export const DOMAIN_FIELD_URL_TARGET_WINDOW = 'URLTargetWindow'; export const DOMAIN_FIELD_LOOKUP_CONTAINER = 'lookupContainer'; export const DOMAIN_FIELD_LOOKUP_QUERY = 'lookupQueryValue'; export const DOMAIN_FIELD_LOOKUP_SCHEMA = 'lookupSchema'; diff --git a/packages/components/src/internal/components/domainproperties/models.tsx b/packages/components/src/internal/components/domainproperties/models.tsx index 50326689f3..28766ccafb 100644 --- a/packages/components/src/internal/components/domainproperties/models.tsx +++ b/packages/components/src/internal/components/domainproperties/models.tsx @@ -914,6 +914,7 @@ export interface IDomainField { uniqueConstraint?: boolean; updatedField: boolean; URL?: string; + URLTargetWindow?: string; visible: boolean; } @@ -985,6 +986,7 @@ export class DomainField required: false, scale: MAX_TEXT_LENGTH, URL: undefined, + URLTargetWindow: undefined, shownInDetailsView: true, shownInInsertView: true, shownInUpdateView: true, @@ -1047,6 +1049,7 @@ export class DomainField declare scale?: number; declare scannable?: boolean; declare URL?: string; + declare URLTargetWindow?: string; declare shownInDetailsView?: boolean; declare shownInInsertView?: boolean; declare shownInUpdateView?: boolean; @@ -1190,6 +1193,11 @@ export class DomainField field.rangeURI = raw.rangeURI; } + // handle URLTargetWindow prop casing mismatch + if (raw['urltargetWindow']) { + field.URLTargetWindow = raw['urltargetWindow']; + } + return field; } @@ -1215,6 +1223,10 @@ export class DomainField json.url = json.URL; delete json.URL; } + if (json.URLTargetWindow !== undefined) { + json.urlTargetWindow = json.URLTargetWindow; + delete json.URLTargetWindow; + } if (json.PHI !== undefined) { json.phi = json.PHI; delete json.PHI; diff --git a/packages/components/src/theme/domainproperties.scss b/packages/components/src/theme/domainproperties.scss index 17bf43d2c0..ab4e697cd3 100644 --- a/packages/components/src/theme/domainproperties.scss +++ b/packages/components/src/theme/domainproperties.scss @@ -359,7 +359,7 @@ margin: 6px 20px 0 0 !important; } -.domain-text-option-scannable { +.domain-text-option-scannable, .domain-text-option-urltargetwindow { display: inline; } From 0e1f744c2374cd498e6bf318693f1940a8c0e374 Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 30 Dec 2025 11:14:48 -0600 Subject: [PATCH 02/19] 7.7.1-fb-urlTargetWindow503.0 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 313278a3a9..a90913831f 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.7.1", + "version": "7.7.1-fb-urlTargetWindow503.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.7.1", + "version": "7.7.1-fb-urlTargetWindow503.0", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index 86bfb757f7..5acb839011 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.7.1", + "version": "7.7.1-fb-urlTargetWindow503.0", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From df04608526dc43f4e29a21d40226a4225370ec2b Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 30 Dec 2025 13:36:46 -0600 Subject: [PATCH 03/19] Fix casing issue with urltargetWindow --- .../src/internal/components/domainproperties/models.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/internal/components/domainproperties/models.tsx b/packages/components/src/internal/components/domainproperties/models.tsx index 28766ccafb..c01053c58a 100644 --- a/packages/components/src/internal/components/domainproperties/models.tsx +++ b/packages/components/src/internal/components/domainproperties/models.tsx @@ -1224,7 +1224,7 @@ export class DomainField delete json.URL; } if (json.URLTargetWindow !== undefined) { - json.urlTargetWindow = json.URLTargetWindow; + json.urltargetWindow = json.URLTargetWindow; // note casing because of GWTPropertyDescriptor behavior on server side delete json.URLTargetWindow; } if (json.PHI !== undefined) { From 1eeee975dc2a3d1b905cafb5edee1ac26a9fd919 Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 30 Dec 2025 13:37:40 -0600 Subject: [PATCH 04/19] 7.7.1-fb-urlTargetWindow503.1 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index a90913831f..7d95edb0ac 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.7.1-fb-urlTargetWindow503.0", + "version": "7.7.1-fb-urlTargetWindow503.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.7.1-fb-urlTargetWindow503.0", + "version": "7.7.1-fb-urlTargetWindow503.1", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index 5acb839011..0c03453298 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.7.1-fb-urlTargetWindow503.0", + "version": "7.7.1-fb-urlTargetWindow503.1", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From 284d8b380fc02ac25cebdfb9b4e6936feacf0b3b Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 30 Dec 2025 14:08:16 -0600 Subject: [PATCH 05/19] urlTargetWindow -> urlTarget --- .../domainproperties/NameAndLinkingOptions.tsx | 14 +++++++------- .../components/domainproperties/constants.ts | 2 +- .../components/domainproperties/models.tsx | 18 +++++++++--------- .../components/src/theme/domainproperties.scss | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx index 5faeea8aa8..5d5c64eb16 100644 --- a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx +++ b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx @@ -16,7 +16,7 @@ import { DOMAIN_FIELD_LABEL, DOMAIN_FIELD_ONTOLOGY_PRINCIPAL_CONCEPT, DOMAIN_FIELD_URL, - DOMAIN_FIELD_URL_TARGET_WINDOW, + DOMAIN_FIELD_URL_TARGET, } from './constants'; import { DomainField, IDomainFormDisplayOptions } from './models'; import { SectionHeading } from './SectionHeading'; @@ -40,7 +40,7 @@ export class NameAndLinkingOptions extends PureComponent { this.props?.onChange(id, value); }; - handleURLTargetWindowChange = (evt: any): void => { + handleURLTargetChange = (evt: any): void => { const id = evt.target.id; const isChecked = evt.target.checked; this.onChange(id, isChecked ? '_blank' : null); @@ -153,11 +153,11 @@ export class NameAndLinkingOptions extends PureComponent {
Open links in a new tab diff --git a/packages/components/src/internal/components/domainproperties/constants.ts b/packages/components/src/internal/components/domainproperties/constants.ts index 7d1511ebf3..80aba4c72c 100644 --- a/packages/components/src/internal/components/domainproperties/constants.ts +++ b/packages/components/src/internal/components/domainproperties/constants.ts @@ -27,7 +27,7 @@ export const DOMAIN_FIELD_DESCRIPTION = 'description'; export const DOMAIN_FIELD_LABEL = 'label'; export const DOMAIN_FIELD_IMPORTALIASES = 'importAliases'; export const DOMAIN_FIELD_URL = 'URL'; -export const DOMAIN_FIELD_URL_TARGET_WINDOW = 'URLTargetWindow'; +export const DOMAIN_FIELD_URL_TARGET = 'URLTarget'; export const DOMAIN_FIELD_LOOKUP_CONTAINER = 'lookupContainer'; export const DOMAIN_FIELD_LOOKUP_QUERY = 'lookupQueryValue'; export const DOMAIN_FIELD_LOOKUP_SCHEMA = 'lookupSchema'; diff --git a/packages/components/src/internal/components/domainproperties/models.tsx b/packages/components/src/internal/components/domainproperties/models.tsx index c01053c58a..9e4c20b49f 100644 --- a/packages/components/src/internal/components/domainproperties/models.tsx +++ b/packages/components/src/internal/components/domainproperties/models.tsx @@ -914,7 +914,7 @@ export interface IDomainField { uniqueConstraint?: boolean; updatedField: boolean; URL?: string; - URLTargetWindow?: string; + URLTarget?: string; visible: boolean; } @@ -986,7 +986,7 @@ export class DomainField required: false, scale: MAX_TEXT_LENGTH, URL: undefined, - URLTargetWindow: undefined, + URLTarget: undefined, shownInDetailsView: true, shownInInsertView: true, shownInUpdateView: true, @@ -1049,7 +1049,7 @@ export class DomainField declare scale?: number; declare scannable?: boolean; declare URL?: string; - declare URLTargetWindow?: string; + declare URLTarget?: string; declare shownInDetailsView?: boolean; declare shownInInsertView?: boolean; declare shownInUpdateView?: boolean; @@ -1193,9 +1193,9 @@ export class DomainField field.rangeURI = raw.rangeURI; } - // handle URLTargetWindow prop casing mismatch - if (raw['urltargetWindow']) { - field.URLTargetWindow = raw['urltargetWindow']; + // handle URLTarget prop casing mismatch + if (raw['urltarget']) { + field.URLTarget = raw['urltarget']; } return field; @@ -1223,9 +1223,9 @@ export class DomainField json.url = json.URL; delete json.URL; } - if (json.URLTargetWindow !== undefined) { - json.urltargetWindow = json.URLTargetWindow; // note casing because of GWTPropertyDescriptor behavior on server side - delete json.URLTargetWindow; + if (json.URLTarget !== undefined) { + json.urltarget = json.URLTarget; // note casing because of GWTPropertyDescriptor behavior on server side + delete json.URLTarget; } if (json.PHI !== undefined) { json.phi = json.PHI; diff --git a/packages/components/src/theme/domainproperties.scss b/packages/components/src/theme/domainproperties.scss index ab4e697cd3..d3633a010e 100644 --- a/packages/components/src/theme/domainproperties.scss +++ b/packages/components/src/theme/domainproperties.scss @@ -359,7 +359,7 @@ margin: 6px 20px 0 0 !important; } -.domain-text-option-scannable, .domain-text-option-urltargetwindow { +.domain-text-option-scannable, .domain-text-option-urltarget { display: inline; } From 17b258bb76b6227a29e1ffa61b6e3b2f88e70aad Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 30 Dec 2025 14:08:56 -0600 Subject: [PATCH 06/19] 7.7.1-fb-urlTargetWindow503.2 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 7d95edb0ac..ffa9614847 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.7.1-fb-urlTargetWindow503.1", + "version": "7.7.1-fb-urlTargetWindow503.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.7.1-fb-urlTargetWindow503.1", + "version": "7.7.1-fb-urlTargetWindow503.2", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index 0c03453298..8dfb895c10 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.7.1-fb-urlTargetWindow503.1", + "version": "7.7.1-fb-urlTargetWindow503.2", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From 33b04e192536853004984c674fc67792b35f19a7 Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 30 Dec 2025 14:47:37 -0600 Subject: [PATCH 07/19] add URLTarget to domain field summary grid --- .../src/internal/components/domainproperties/models.tsx | 9 ++++++++- .../components/domainproperties/propertiesUtil.ts | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/components/src/internal/components/domainproperties/models.tsx b/packages/components/src/internal/components/domainproperties/models.tsx index 9e4c20b49f..c199713296 100644 --- a/packages/components/src/internal/components/domainproperties/models.tsx +++ b/packages/components/src/internal/components/domainproperties/models.tsx @@ -549,7 +549,14 @@ export class DomainDesign if (!showFilterCriteria) delete columns.filterCriteria; const unsortedColumns = List( - Object.keys(columns).map(key => ({ index: key, caption: camelCaseToTitleCase(key), sortable: true })) + Object.keys(columns).map(key => { + let caption = camelCaseToTitleCase(key); + // special case for url and urltarget + if (key.toLowerCase() === 'url') caption = 'URL'; + if (key.toLowerCase() === 'urltarget') caption = 'URL Target'; + + return { index: key, caption, sortable: true }; + }) ); return specialCols.concat(unsortedColumns.sort(reorderSummaryColumns)).toList(); } diff --git a/packages/components/src/internal/components/domainproperties/propertiesUtil.ts b/packages/components/src/internal/components/domainproperties/propertiesUtil.ts index 1858fb1dda..9efbce3b2a 100644 --- a/packages/components/src/internal/components/domainproperties/propertiesUtil.ts +++ b/packages/components/src/internal/components/domainproperties/propertiesUtil.ts @@ -147,6 +147,7 @@ export function reorderSummaryColumns(a: DomainPropertiesGridColumn, b: DomainPr 'label', 'importAliases', 'url', + 'urltarget', 'conditionalFormats', 'propertyValidators', 'valueExpression', From 9b7ede1105061ddf78a81e925e364270792eb244 Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 30 Dec 2025 14:48:10 -0600 Subject: [PATCH 08/19] 7.7.1-fb-urlTargetWindow503.3 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index ffa9614847..ca46566fff 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.7.1-fb-urlTargetWindow503.2", + "version": "7.7.1-fb-urlTargetWindow503.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.7.1-fb-urlTargetWindow503.2", + "version": "7.7.1-fb-urlTargetWindow503.3", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index 8dfb895c10..437ccf70d8 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.7.1-fb-urlTargetWindow503.2", + "version": "7.7.1-fb-urlTargetWindow503.3", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From 103b1d396f9455acfcae8b0bed172df563d72e98 Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 30 Dec 2025 15:56:44 -0600 Subject: [PATCH 09/19] jest test updates --- .../NameAndLinkingOptions.test.tsx | 34 +++++++++++++++++++ .../NameAndLinkingOptions.test.tsx.snap | 14 ++++++++ .../domainproperties/models.test.ts | 9 +++++ 3 files changed, 57 insertions(+) diff --git a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx index beaafddf61..4511cec5cd 100644 --- a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx +++ b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx @@ -5,10 +5,12 @@ import { createFormInputId } from './utils'; import { CALCULATED_CONCEPT_URI, DOMAIN_FIELD_DESCRIPTION, + DOMAIN_FIELD_FULLY_LOCKED, DOMAIN_FIELD_IMPORTALIASES, DOMAIN_FIELD_LABEL, DOMAIN_FIELD_ONTOLOGY_PRINCIPAL_CONCEPT, DOMAIN_FIELD_URL, + DOMAIN_FIELD_URL_TARGET, STORAGE_UNIQUE_ID_CONCEPT_URI, STRING_RANGE_URI, } from './constants'; @@ -29,6 +31,7 @@ const field = DomainField.create({ label: _label, importAliases: _importAliases, URL: _URL, + URLTarget: '_blank', propertyURI: 'test', }); @@ -50,6 +53,15 @@ const calculatedField = DomainField.create({ conceptURI: CALCULATED_CONCEPT_URI, }); +const lockedField = DomainField.create({ + name: 'lockedField', + rangeURI: STRING_RANGE_URI, + propertyId: 3, + description: 'locked field desc', + label: 'Locked Field', + lockType: DOMAIN_FIELD_FULLY_LOCKED, +}); + const DEFAULT_PROPS = { index: 1, domainIndex: 1, @@ -73,21 +85,31 @@ describe('NameAndLinkingOptions', () => { let formField = document.querySelectorAll('#' + createFormInputId(DOMAIN_FIELD_DESCRIPTION, 1, 1)); expect(formField.length).toEqual(1); expect(formField[0].textContent).toEqual(_description); + expect(formField[0].hasAttribute('disabled')).toEqual(false); // Label formField = document.querySelectorAll('#' + createFormInputId(DOMAIN_FIELD_LABEL, 1, 1)); expect(formField.length).toEqual(1); expect(formField[0].getAttribute('value')).toEqual(_label); + expect(formField[0].hasAttribute('disabled')).toEqual(false); // Aliases formField = document.querySelectorAll('#' + createFormInputId(DOMAIN_FIELD_IMPORTALIASES, 1, 1)); expect(formField.length).toEqual(1); expect(formField[0].getAttribute('value')).toEqual(_importAliases); + expect(formField[0].hasAttribute('disabled')).toEqual(false); // URL formField = document.querySelectorAll('#' + createFormInputId(DOMAIN_FIELD_URL, 1, 1)); expect(formField.length).toEqual(1); expect(formField[0].getAttribute('value')).toEqual(_URL); + expect(formField[0].hasAttribute('disabled')).toEqual(false); + + // URL Target + formField = document.querySelectorAll('#' + createFormInputId(DOMAIN_FIELD_URL_TARGET, 1, 1)); + expect(formField.length).toEqual(1); + expect(formField[0].hasAttribute('checked')).toEqual(true); + expect(formField[0].hasAttribute('disabled')).toEqual(false); expect(container).toMatchSnapshot(); }); @@ -119,6 +141,9 @@ describe('NameAndLinkingOptions', () => { test('calculated field', () => { render(); expect(document.querySelectorAll('#' + createFormInputId(DOMAIN_FIELD_IMPORTALIASES, 1, 1))).toHaveLength(0); + + expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL, 1, 1)).getAttribute('value')).toEqual(''); + expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL_TARGET, 1, 1)).hasAttribute('checked')).toEqual(false); }); test('hideImportAliases', () => { @@ -132,4 +157,13 @@ describe('NameAndLinkingOptions', () => { ); expect(document.querySelectorAll('#' + createFormInputId(DOMAIN_FIELD_IMPORTALIASES, 1, 1))).toHaveLength(0); }); + + test('locked field', () => { + render(); + expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_LABEL, 1, 1)).hasAttribute('disabled')).toEqual(true); + expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_DESCRIPTION, 1, 1)).hasAttribute('disabled')).toEqual(true); + expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_IMPORTALIASES, 1, 1)).hasAttribute('disabled')).toEqual(true); + expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL, 1, 1)).hasAttribute('disabled')).toEqual(true); + expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL_TARGET, 1, 1)).hasAttribute('disabled')).toEqual(true); + }); }); diff --git a/packages/components/src/internal/components/domainproperties/__snapshots__/NameAndLinkingOptions.test.tsx.snap b/packages/components/src/internal/components/domainproperties/__snapshots__/NameAndLinkingOptions.test.tsx.snap index 4ac4b5d759..6d6ea29fa3 100644 --- a/packages/components/src/internal/components/domainproperties/__snapshots__/NameAndLinkingOptions.test.tsx.snap +++ b/packages/components/src/internal/components/domainproperties/__snapshots__/NameAndLinkingOptions.test.tsx.snap @@ -110,6 +110,20 @@ exports[`NameAndLinkingOptions Name and Linking options 1`] = ` type="text" value="This is a URL" /> +
+ + + Open links in a new tab + +
diff --git a/packages/components/src/internal/components/domainproperties/models.test.ts b/packages/components/src/internal/components/domainproperties/models.test.ts index 1e4adbe6e1..1b5e02acdc 100644 --- a/packages/components/src/internal/components/domainproperties/models.test.ts +++ b/packages/components/src/internal/components/domainproperties/models.test.ts @@ -122,6 +122,7 @@ const gridDataAppPropsOnlyConst = [ scale: 4000, name: 'a', URL: '', + URLTarget: '', conceptURI: '', rangeURI: 'http://www.w3.org/2001/XMLSchema#int', PHI: '', @@ -188,6 +189,7 @@ const nameCol = new GridColumn({ const gridColumnsConst = [ selectionCol, nameCol, + { index: 'URLTarget', caption: 'URL Target', sortable: true }, { index: 'URL', caption: 'URL', sortable: true }, { index: 'PHI', caption: 'PHI', sortable: true }, { index: 'rangeURI', caption: 'Range URI', sortable: true }, @@ -1490,6 +1492,13 @@ describe('resolveBaseProperties', () => { DOMAIN_FIELD_PARTIALLY_LOCKED ); }); + + test('URLTarget', () => { + expect(DomainField.resolveBaseProperties({}).URLTarget).toBeUndefined(); + expect(DomainField.resolveBaseProperties({ URLTarget: '_self' }).URLTarget).toBeUndefined(); + expect(DomainField.resolveBaseProperties({ urltarget: '_self' }).URLTarget).toBe('_self'); + expect(DomainField.resolveBaseProperties({ urltarget: '_blank' }).URLTarget).toBe('_blank'); + }); }); describe('resolveLookupQueryValue', () => { From ec297a4394adbd8fda32f1b6f80da7fca7fbcbdd Mon Sep 17 00:00:00 2001 From: cnathe Date: Fri, 2 Jan 2026 10:39:09 -0600 Subject: [PATCH 10/19] 7.7.3-fb-urlTargetWindow503.0 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 08e20f7140..d4b8f1e16d 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.7.3", + "version": "7.7.3-fb-urlTargetWindow503.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.7.3", + "version": "7.7.3-fb-urlTargetWindow503.0", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index ae0ca2eba2..69cb3c3a95 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.7.3", + "version": "7.7.3-fb-urlTargetWindow503.0", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From c9b3e7b55f58900af1c6343fc2f18a06801c4158 Mon Sep 17 00:00:00 2001 From: cnathe Date: Mon, 5 Jan 2026 10:25:45 -0600 Subject: [PATCH 11/19] clear "open in new tab" checkbox if URL is cleared --- .../DomainRowExpandedOptions.tsx | 2 ++ .../NameAndLinkingOptions.tsx | 28 +++++++++++++++---- .../components/domainproperties/utils.test.ts | 12 +++++++- .../components/domainproperties/utils.ts | 4 +++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/components/src/internal/components/domainproperties/DomainRowExpandedOptions.tsx b/packages/components/src/internal/components/domainproperties/DomainRowExpandedOptions.tsx index 748e233498..ef27ca621b 100644 --- a/packages/components/src/internal/components/domainproperties/DomainRowExpandedOptions.tsx +++ b/packages/components/src/internal/components/domainproperties/DomainRowExpandedOptions.tsx @@ -265,6 +265,7 @@ export class DomainRowExpandedOptions extends React.Component { field, index, onChange, + onMultiChange, showingModal, appPropertiesOnly, domainIndex, @@ -311,6 +312,7 @@ export class DomainRowExpandedOptions extends React.Component { domainIndex={domainIndex} field={field} onChange={onChange} + onMultiChange={onMultiChange} appPropertiesOnly={appPropertiesOnly} domainFormDisplayOptions={domainFormDisplayOptions} /> diff --git a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx index 5d5c64eb16..a4a6fc9750 100644 --- a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx +++ b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx @@ -1,4 +1,5 @@ import React, { PureComponent, ReactNode } from 'react'; +import {List} from "immutable"; import { HelpLink, URL_ENCODING_TOPIC } from '../../util/helpLinks'; @@ -9,7 +10,7 @@ import { ONTOLOGY_MODULE_NAME } from '../ontology/actions'; import { hasModule } from '../../app/utils'; import { isFieldFullyLocked } from './propertiesUtil'; -import { createFormInputId, createFormInputName } from './utils'; +import { createFormInputId, createFormInputName, isEmptyString } from './utils'; import { DOMAIN_FIELD_DESCRIPTION, DOMAIN_FIELD_IMPORTALIASES, @@ -18,7 +19,7 @@ import { DOMAIN_FIELD_URL, DOMAIN_FIELD_URL_TARGET, } from './constants'; -import { DomainField, IDomainFormDisplayOptions } from './models'; +import {DomainField, IDomainFormDisplayOptions, IFieldChange} from './models'; import { SectionHeading } from './SectionHeading'; import { DomainFieldLabel } from './DomainFieldLabel'; @@ -29,6 +30,7 @@ interface NameAndLinkingProps { field: DomainField; index: number; onChange: (string, any) => void; + onMultiChange: (changes: List) => void; } export class NameAndLinkingOptions extends PureComponent { @@ -37,7 +39,23 @@ export class NameAndLinkingOptions extends PureComponent { }; onChange = (id: string, value: any): void => { - this.props?.onChange(id, value); + this.props.onChange(id, value); + }; + + handleURLChange = (evt: any): void => { + const { index, domainIndex } = this.props; + const val = evt.target.value; + const isEmpty = isEmptyString(val); + + // make sure to uncheck the "open in new tab" option if URL is cleared out + if (isEmpty) { + let changes = List(); + changes = changes.push({ id: evt.target.id, value: null }); + changes = changes.push({ id: createFormInputId(DOMAIN_FIELD_URL_TARGET, domainIndex, index), value: null }); + this.props.onMultiChange(changes); + } else { + this.onChange(evt.target.id, isEmpty ? null : val); + } }; handleURLTargetChange = (evt: any): void => { @@ -146,7 +164,7 @@ export class NameAndLinkingOptions extends PureComponent { value={field.URL || ''} id={createFormInputId(DOMAIN_FIELD_URL, domainIndex, index)} name={createFormInputName(DOMAIN_FIELD_URL)} - onChange={this.handleChange} + onChange={this.handleURLChange} disabled={isFieldFullyLocked(field.lockType)} /> {/*GitHub Issue 503: Field editor URL option to set target window (i.e. _blank)*/} @@ -158,7 +176,7 @@ export class NameAndLinkingOptions extends PureComponent { className="form-control domain-text-option-urltarget" checked={field.URLTarget === '_blank'} onChange={this.handleURLTargetChange} - disabled={isFieldFullyLocked(field.lockType)} + disabled={isFieldFullyLocked(field.lockType) || isEmptyString(field.URL)} /> Open links in a new tab diff --git a/packages/components/src/internal/components/domainproperties/utils.test.ts b/packages/components/src/internal/components/domainproperties/utils.test.ts index d0b87f1485..7154c48279 100644 --- a/packages/components/src/internal/components/domainproperties/utils.test.ts +++ b/packages/components/src/internal/components/domainproperties/utils.test.ts @@ -2,7 +2,7 @@ import { Set, OrderedMap } from 'immutable'; import { IParentAlias } from '../entities/models'; -import { getDuplicateAlias, parentAliasInvalid } from './utils'; +import {getDuplicateAlias, isEmptyString, parentAliasInvalid} from './utils'; describe('domain property utils', () => { test('parentAliasInvalid', () => { @@ -63,3 +63,13 @@ describe('getDuplicateAlias', () => { expect(getDuplicateAlias(parentAliases, true)).toEqual(new Set(['alias1'])); }); }); + +describe('isEmptyString', () => { + test('various values', () => { + expect(isEmptyString(undefined)).toBe(true); + expect(isEmptyString(null)).toBe(true); + expect(isEmptyString('')).toBe(true); + expect(isEmptyString(' ')).toBe(true); + expect(isEmptyString('not empty')).toBe(false); + }); +}); diff --git a/packages/components/src/internal/components/domainproperties/utils.ts b/packages/components/src/internal/components/domainproperties/utils.ts index 5baa1e2427..eb92db8885 100644 --- a/packages/components/src/internal/components/domainproperties/utils.ts +++ b/packages/components/src/internal/components/domainproperties/utils.ts @@ -121,3 +121,7 @@ export function parentAliasInvalid(alias: Partial): boolean { export function getSourceDomainDefaultSystemFields(): SystemField[] { return SOURCE_DOMAIN_SYSTEM_FIELDS; } + +export function isEmptyString(value: string): boolean { + return !value || value.trim() === ''; +} From 14dc464681785fc802863a053c2f0a1aa8ad9378 Mon Sep 17 00:00:00 2001 From: cnathe Date: Mon, 5 Jan 2026 10:25:57 -0600 Subject: [PATCH 12/19] 7.7.3-fb-urlTargetWindow503.1 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index d4b8f1e16d..476bbaacad 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.7.3-fb-urlTargetWindow503.0", + "version": "7.7.3-fb-urlTargetWindow503.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.7.3-fb-urlTargetWindow503.0", + "version": "7.7.3-fb-urlTargetWindow503.1", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index 69cb3c3a95..beab8bc2a0 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.7.3-fb-urlTargetWindow503.0", + "version": "7.7.3-fb-urlTargetWindow503.1", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From f5dab362bace4e6ce1faf45fd542bcdb0dee32ce Mon Sep 17 00:00:00 2001 From: cnathe Date: Mon, 5 Jan 2026 14:22:06 -0600 Subject: [PATCH 13/19] 7.8.0-fb-urlTargetWindow503.0 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 702814349b..b4f2b2fd8c 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.8.0", + "version": "7.8.0-fb-urlTargetWindow503.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.8.0", + "version": "7.8.0-fb-urlTargetWindow503.0", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index 3ccc5e2e7c..cca219aaca 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.8.0", + "version": "7.8.0-fb-urlTargetWindow503.0", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From a0dff0c8e31fde9fede9b6138d215cf5b332b549 Mon Sep 17 00:00:00 2001 From: cnathe Date: Mon, 5 Jan 2026 15:46:35 -0600 Subject: [PATCH 14/19] URLTarget prop name to isTargetBlank --- .../NameAndLinkingOptions.test.tsx | 2 +- .../NameAndLinkingOptions.tsx | 10 ++++------ .../NameAndLinkingOptions.test.tsx.snap | 6 +++--- .../components/domainproperties/constants.ts | 2 +- .../domainproperties/models.test.ts | 15 ++++++++------- .../components/domainproperties/models.tsx | 19 ++++++++++--------- .../src/theme/domainproperties.scss | 2 +- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx index 4511cec5cd..cddc94b09e 100644 --- a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx +++ b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx @@ -31,7 +31,7 @@ const field = DomainField.create({ label: _label, importAliases: _importAliases, URL: _URL, - URLTarget: '_blank', + isTargetBlank: true, propertyURI: 'test', }); diff --git a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx index a4a6fc9750..11f0d1b446 100644 --- a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx +++ b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx @@ -51,7 +51,7 @@ export class NameAndLinkingOptions extends PureComponent { if (isEmpty) { let changes = List(); changes = changes.push({ id: evt.target.id, value: null }); - changes = changes.push({ id: createFormInputId(DOMAIN_FIELD_URL_TARGET, domainIndex, index), value: null }); + changes = changes.push({ id: createFormInputId(DOMAIN_FIELD_URL_TARGET, domainIndex, index), value: false }); this.props.onMultiChange(changes); } else { this.onChange(evt.target.id, isEmpty ? null : val); @@ -59,9 +59,7 @@ export class NameAndLinkingOptions extends PureComponent { }; handleURLTargetChange = (evt: any): void => { - const id = evt.target.id; - const isChecked = evt.target.checked; - this.onChange(id, isChecked ? '_blank' : null); + this.onChange(evt.target.id, evt.target.checked); }; getImportAliasHelpText = (): ReactNode => { @@ -173,8 +171,8 @@ export class NameAndLinkingOptions extends PureComponent { type="checkbox" id={createFormInputId(DOMAIN_FIELD_URL_TARGET, domainIndex, index)} name={createFormInputName(DOMAIN_FIELD_URL_TARGET)} - className="form-control domain-text-option-urltarget" - checked={field.URLTarget === '_blank'} + className="form-control domain-text-option-istargetblank" + checked={field.isTargetBlank} onChange={this.handleURLTargetChange} disabled={isFieldFullyLocked(field.lockType) || isEmptyString(field.URL)} /> diff --git a/packages/components/src/internal/components/domainproperties/__snapshots__/NameAndLinkingOptions.test.tsx.snap b/packages/components/src/internal/components/domainproperties/__snapshots__/NameAndLinkingOptions.test.tsx.snap index 6d6ea29fa3..cc792456f6 100644 --- a/packages/components/src/internal/components/domainproperties/__snapshots__/NameAndLinkingOptions.test.tsx.snap +++ b/packages/components/src/internal/components/domainproperties/__snapshots__/NameAndLinkingOptions.test.tsx.snap @@ -115,9 +115,9 @@ exports[`NameAndLinkingOptions Name and Linking options 1`] = ` > diff --git a/packages/components/src/internal/components/domainproperties/constants.ts b/packages/components/src/internal/components/domainproperties/constants.ts index 80aba4c72c..c4f60ed49f 100644 --- a/packages/components/src/internal/components/domainproperties/constants.ts +++ b/packages/components/src/internal/components/domainproperties/constants.ts @@ -27,7 +27,7 @@ export const DOMAIN_FIELD_DESCRIPTION = 'description'; export const DOMAIN_FIELD_LABEL = 'label'; export const DOMAIN_FIELD_IMPORTALIASES = 'importAliases'; export const DOMAIN_FIELD_URL = 'URL'; -export const DOMAIN_FIELD_URL_TARGET = 'URLTarget'; +export const DOMAIN_FIELD_URL_TARGET = 'isTargetBlank'; export const DOMAIN_FIELD_LOOKUP_CONTAINER = 'lookupContainer'; export const DOMAIN_FIELD_LOOKUP_QUERY = 'lookupQueryValue'; export const DOMAIN_FIELD_LOOKUP_SCHEMA = 'lookupSchema'; diff --git a/packages/components/src/internal/components/domainproperties/models.test.ts b/packages/components/src/internal/components/domainproperties/models.test.ts index 9c1c2955f3..aca1a45c00 100644 --- a/packages/components/src/internal/components/domainproperties/models.test.ts +++ b/packages/components/src/internal/components/domainproperties/models.test.ts @@ -122,7 +122,7 @@ const gridDataAppPropsOnlyConst = [ scale: 4000, name: 'a', URL: '', - URLTarget: '', + urltarget: '', conceptURI: '', rangeURI: 'http://www.w3.org/2001/XMLSchema#int', PHI: '', @@ -189,7 +189,6 @@ const nameCol = new GridColumn({ const gridColumnsConst = [ selectionCol, nameCol, - { index: 'URLTarget', caption: 'URL Target', sortable: true }, { index: 'URL', caption: 'URL', sortable: true }, { index: 'PHI', caption: 'PHI', sortable: true }, { index: 'rangeURI', caption: 'Range URI', sortable: true }, @@ -209,6 +208,7 @@ const gridColumnsConst = [ { index: 'description', caption: 'Description', sortable: true }, { index: 'label', caption: 'Label', sortable: true }, { index: 'importAliases', caption: 'Import Aliases', sortable: true }, + { index: 'urltarget', caption: 'URL Target', sortable: true }, { index: 'conditionalFormats', caption: 'Conditional Formats', @@ -1493,11 +1493,12 @@ describe('resolveBaseProperties', () => { ); }); - test('URLTarget', () => { - expect(DomainField.resolveBaseProperties({}).URLTarget).toBeUndefined(); - expect(DomainField.resolveBaseProperties({ URLTarget: '_self' }).URLTarget).toBeUndefined(); - expect(DomainField.resolveBaseProperties({ urltarget: '_self' }).URLTarget).toBe('_self'); - expect(DomainField.resolveBaseProperties({ urltarget: '_blank' }).URLTarget).toBe('_blank'); + test('isTargetBlank', () => { + expect(DomainField.resolveBaseProperties({}).isTargetBlank).toBeUndefined(); + expect(DomainField.resolveBaseProperties({ urltarget: '_self' }).isTargetBlank).toBeUndefined(); + expect(DomainField.resolveBaseProperties({ URLTarget: '_self' }).isTargetBlank).toBeUndefined(); + expect(DomainField.resolveBaseProperties({ urltarget: '_blank' }).isTargetBlank).toBe(true); + expect(DomainField.resolveBaseProperties({ URLTarget: '_blank' }).isTargetBlank).toBe(true); }); }); diff --git a/packages/components/src/internal/components/domainproperties/models.tsx b/packages/components/src/internal/components/domainproperties/models.tsx index c199713296..a0c2fbe2d4 100644 --- a/packages/components/src/internal/components/domainproperties/models.tsx +++ b/packages/components/src/internal/components/domainproperties/models.tsx @@ -921,7 +921,7 @@ export interface IDomainField { uniqueConstraint?: boolean; updatedField: boolean; URL?: string; - URLTarget?: string; + isTargetBlank?: boolean; visible: boolean; } @@ -993,7 +993,7 @@ export class DomainField required: false, scale: MAX_TEXT_LENGTH, URL: undefined, - URLTarget: undefined, + isTargetBlank: false, shownInDetailsView: true, shownInInsertView: true, shownInUpdateView: true, @@ -1056,7 +1056,7 @@ export class DomainField declare scale?: number; declare scannable?: boolean; declare URL?: string; - declare URLTarget?: string; + declare isTargetBlank?: boolean; declare shownInDetailsView?: boolean; declare shownInInsertView?: boolean; declare shownInUpdateView?: boolean; @@ -1201,8 +1201,8 @@ export class DomainField } // handle URLTarget prop casing mismatch - if (raw['urltarget']) { - field.URLTarget = raw['urltarget']; + if (raw['urltarget'] === '_blank' || raw['URLTarget'] === '_blank') { + field.isTargetBlank = true; } return field; @@ -1224,16 +1224,17 @@ export class DomainField json.lookupContainer = null; } + if (json.hasOwnProperty('isTargetBlank')) { + json.urltarget = json.isTargetBlank ? '_blank' : null; + delete json.isTargetBlank; + } + // for some reason the property binding server side cares about casing here for 'URL' and 'PHI' if (fixCaseSensitivity) { if (json.URL !== undefined) { json.url = json.URL; delete json.URL; } - if (json.URLTarget !== undefined) { - json.urltarget = json.URLTarget; // note casing because of GWTPropertyDescriptor behavior on server side - delete json.URLTarget; - } if (json.PHI !== undefined) { json.phi = json.PHI; delete json.PHI; diff --git a/packages/components/src/theme/domainproperties.scss b/packages/components/src/theme/domainproperties.scss index d3633a010e..98f1a5914f 100644 --- a/packages/components/src/theme/domainproperties.scss +++ b/packages/components/src/theme/domainproperties.scss @@ -359,7 +359,7 @@ margin: 6px 20px 0 0 !important; } -.domain-text-option-scannable, .domain-text-option-urltarget { +.domain-text-option-scannable, .domain-text-option-istargetblank { display: inline; } From 49b44928967d8b697e3fbd217ed4658f1ffae541 Mon Sep 17 00:00:00 2001 From: cnathe Date: Mon, 5 Jan 2026 15:47:00 -0600 Subject: [PATCH 15/19] 7.8.0-fb-urlTargetWindow503.1 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index b4f2b2fd8c..a702bbec26 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.8.0-fb-urlTargetWindow503.0", + "version": "7.8.0-fb-urlTargetWindow503.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.8.0-fb-urlTargetWindow503.0", + "version": "7.8.0-fb-urlTargetWindow503.1", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index cca219aaca..8b5dd88571 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.8.0-fb-urlTargetWindow503.0", + "version": "7.8.0-fb-urlTargetWindow503.1", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From 9f68f0d7878de5106f1711970d142b44874218a9 Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 6 Jan 2026 08:52:00 -0600 Subject: [PATCH 16/19] 7.8.1-fb-urlTargetWindow503.0 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index ac63174a57..9be30398e8 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.8.1", + "version": "7.8.1-fb-urlTargetWindow503.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.8.1", + "version": "7.8.1-fb-urlTargetWindow503.0", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index 8add6202e4..0c4015cf4d 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.8.1", + "version": "7.8.1-fb-urlTargetWindow503.0", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From a05af946287cdbd595573d937665d5a6e1690b08 Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 6 Jan 2026 10:46:30 -0600 Subject: [PATCH 17/19] npm run lint-branch-fix --- .../NameAndLinkingOptions.test.tsx | 28 ++++++++--- .../NameAndLinkingOptions.tsx | 47 ++++++++++--------- .../components/domainproperties/models.tsx | 2 +- .../components/domainproperties/utils.test.ts | 4 +- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx index cddc94b09e..e17502eac4 100644 --- a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx +++ b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.test.tsx @@ -142,8 +142,12 @@ describe('NameAndLinkingOptions', () => { render(); expect(document.querySelectorAll('#' + createFormInputId(DOMAIN_FIELD_IMPORTALIASES, 1, 1))).toHaveLength(0); - expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL, 1, 1)).getAttribute('value')).toEqual(''); - expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL_TARGET, 1, 1)).hasAttribute('checked')).toEqual(false); + expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL, 1, 1)).getAttribute('value')).toEqual( + '' + ); + expect( + document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL_TARGET, 1, 1)).hasAttribute('checked') + ).toEqual(false); }); test('hideImportAliases', () => { @@ -160,10 +164,20 @@ describe('NameAndLinkingOptions', () => { test('locked field', () => { render(); - expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_LABEL, 1, 1)).hasAttribute('disabled')).toEqual(true); - expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_DESCRIPTION, 1, 1)).hasAttribute('disabled')).toEqual(true); - expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_IMPORTALIASES, 1, 1)).hasAttribute('disabled')).toEqual(true); - expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL, 1, 1)).hasAttribute('disabled')).toEqual(true); - expect(document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL_TARGET, 1, 1)).hasAttribute('disabled')).toEqual(true); + expect( + document.querySelector('#' + createFormInputId(DOMAIN_FIELD_LABEL, 1, 1)).hasAttribute('disabled') + ).toEqual(true); + expect( + document.querySelector('#' + createFormInputId(DOMAIN_FIELD_DESCRIPTION, 1, 1)).hasAttribute('disabled') + ).toEqual(true); + expect( + document.querySelector('#' + createFormInputId(DOMAIN_FIELD_IMPORTALIASES, 1, 1)).hasAttribute('disabled') + ).toEqual(true); + expect( + document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL, 1, 1)).hasAttribute('disabled') + ).toEqual(true); + expect( + document.querySelector('#' + createFormInputId(DOMAIN_FIELD_URL_TARGET, 1, 1)).hasAttribute('disabled') + ).toEqual(true); }); }); diff --git a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx index 11f0d1b446..4f2e195824 100644 --- a/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx +++ b/packages/components/src/internal/components/domainproperties/NameAndLinkingOptions.tsx @@ -1,5 +1,5 @@ import React, { PureComponent, ReactNode } from 'react'; -import {List} from "immutable"; +import { List } from 'immutable'; import { HelpLink, URL_ENCODING_TOPIC } from '../../util/helpLinks'; @@ -19,7 +19,7 @@ import { DOMAIN_FIELD_URL, DOMAIN_FIELD_URL_TARGET, } from './constants'; -import {DomainField, IDomainFormDisplayOptions, IFieldChange} from './models'; +import { DomainField, IDomainFormDisplayOptions, IFieldChange } from './models'; import { SectionHeading } from './SectionHeading'; import { DomainFieldLabel } from './DomainFieldLabel'; @@ -51,7 +51,10 @@ export class NameAndLinkingOptions extends PureComponent { if (isEmpty) { let changes = List(); changes = changes.push({ id: evt.target.id, value: null }); - changes = changes.push({ id: createFormInputId(DOMAIN_FIELD_URL_TARGET, domainIndex, index), value: false }); + changes = changes.push({ + id: createFormInputId(DOMAIN_FIELD_URL_TARGET, domainIndex, index), + value: false, + }); this.props.onMultiChange(changes); } else { this.onChange(evt.target.id, isEmpty ? null : val); @@ -93,7 +96,7 @@ export class NameAndLinkingOptions extends PureComponent {
- +
@@ -101,24 +104,24 @@ export class NameAndLinkingOptions extends PureComponent {
Description