diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index c5f8c41b94..79cd4e3cb3 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.55.5", + "version": "7.56.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.55.5", + "version": "7.56.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 97c0f58857..864dd65bf6 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.55.5", + "version": "7.56.0", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index c0000e2339..7e65104f96 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -651,6 +651,7 @@ import { DEFAULT_SAMPLE_FIELD_CONFIG, FIND_BY_IDS_QUERY_PARAM, IS_ALIQUOT_COL, + SAMPLE_COLOR_COLOR_COLUMN_NAME, SAMPLE_COLOR_COLUMN_NAME, SAMPLE_COLOR_REQUIRED_COLUMNS, SAMPLE_DATA_EXPORT_CONFIG, @@ -714,6 +715,7 @@ import { getFolderSampleTypeExclusion, getPrimaryAppProperties, getProjectPath, + hasActiveProjectColors, hasModule, hasPremiumModule, hasProductFolders, @@ -741,7 +743,6 @@ import { isProjectContainer, isProtectedDataEnabled, isRegistryEnabled, - isSampleColorsEnabled, isSampleStatusEnabled, isSharedContainer, isSourceTypeEnabled, @@ -958,7 +959,6 @@ const App = { isSharedContainer, freezerManagerIsCurrentApp, isSampleManagerEnabled, - isSampleColorsEnabled, isSampleStatusEnabled, isProductFoldersEnabled, isAllProductFoldersFilteringEnabled, @@ -976,6 +976,7 @@ const App = { getLabelsTestAPIWrapper, getSecurityTestAPIWrapper, getQueryTestAPIWrapper, + hasActiveProjectColors, hasPremiumModule, hasProductFolders, hasModule, @@ -1663,6 +1664,7 @@ export { ResponsiveMenuButton, ResponsiveMenuButtonGroup, runDetailsColumnsForQueryModel, + SAMPLE_COLOR_COLOR_COLUMN_NAME, SAMPLE_COLOR_COLUMN_NAME, SAMPLE_COLOR_REQUIRED_COLUMNS, SAMPLE_DATA_EXPORT_CONFIG, diff --git a/packages/components/src/internal/app/constants.ts b/packages/components/src/internal/app/constants.ts index 2ac3de755e..6994e15ab4 100644 --- a/packages/components/src/internal/app/constants.ts +++ b/packages/components/src/internal/app/constants.ts @@ -114,6 +114,7 @@ export const EXPERIMENTAL_REQUESTS_MENU = 'experimental-biologics-requests-menu' export const FOLDER_DATA_TYPE_EXCLUSIONS = 'dataTypeExclusions'; export const ARCHIVED_FOLDERS = 'archivedContainers'; +export const HAS_ACTIVE_PROJECT_COLORS = 'hasActiveProjectColors'; export const BASE_APP_HELP_LINK = 'https://www.labkey.org/SampleManagerHelp/wiki-page.view?name='; diff --git a/packages/components/src/internal/app/utils.test.ts b/packages/components/src/internal/app/utils.test.ts index 7869731379..6095f5baa7 100644 --- a/packages/components/src/internal/app/utils.test.ts +++ b/packages/components/src/internal/app/utils.test.ts @@ -47,6 +47,7 @@ import { getProjectPath, getSamplesSectionConfig, getStorageSectionConfig, + hasActiveProjectColors, hasPremiumModule, isAppHomeFolder, isAssayEnabled, @@ -812,6 +813,18 @@ describe('utils', () => { expect(LABKEY.moduleContext.query).toEqual({ hasProductFolders: false, ken: 'griffey' }); }); + test('hasActiveProjectColors', () => { + expect(hasActiveProjectColors({})).toBe(false); + expect(hasActiveProjectColors({ samplemanagement: {} })).toBe(false); + expect(hasActiveProjectColors({ samplemanagement: { hasActiveProjectColors: false } })).toBe(false); + expect(hasActiveProjectColors({ samplemanagement: { hasActiveProjectColors: true } })).toBe(true); + + const moduleContext = LABKEY.moduleContext; + LABKEY.moduleContext = { samplemanagement: { hasActiveProjectColors: true } }; + expect(hasActiveProjectColors()).toBe(true); // falls back to the global moduleContext + LABKEY.moduleContext = moduleContext; + }); + test('hasPremiumModule', () => { expect(hasPremiumModule({})).toBeFalsy(); expect(hasPremiumModule({ api: { moduleNames: ['samplemanagement'] } })).toBeFalsy(); diff --git a/packages/components/src/internal/app/utils.ts b/packages/components/src/internal/app/utils.ts index 716e2ed344..b9df29c2ad 100644 --- a/packages/components/src/internal/app/utils.ts +++ b/packages/components/src/internal/app/utils.ts @@ -30,6 +30,7 @@ import { FOLDER_DATA_TYPE_EXCLUSIONS, FREEZER_MANAGER_APP_PROPERTIES, FREEZERS_KEY, + HAS_ACTIVE_PROJECT_COLORS, HOME_KEY, LABKEY_SERVER_PRODUCT_NAME, LIMS_APP_PROPERTIES, @@ -204,8 +205,20 @@ export function isSampleStatusEnabled(moduleContext?: ModuleContext): boolean { return hasSampleManagementModule(moduleContext); } -export function isSampleColorsEnabled(moduleContext?: ModuleContext): boolean { - return resolveModuleContext(moduleContext)?.experiment?.SampleColors === true; +/** True if the current container's project has at least one active (non-archived) sample color. */ +export function hasActiveProjectColors(moduleContext?: ModuleContext): boolean { + return resolveModuleContext(moduleContext)?.samplemanagement?.[HAS_ACTIVE_PROJECT_COLORS] === true; +} + +export function setActiveProjectColors(moduleContext: ModuleContext, hasActiveProjectColors: boolean): ModuleContext { + // side-effect set global moduleContext + if (LABKEY?.moduleContext?.samplemanagement) { + LABKEY.moduleContext.samplemanagement.hasActiveProjectColors = hasActiveProjectColors; + } + + return Object.assign(moduleContext ?? {}, { + samplemanagement: Object.assign(moduleContext?.samplemanagement ?? {}, { hasActiveProjectColors }), + }); } export function isQueryMetadataEditor(): boolean { diff --git a/packages/components/src/internal/components/chart/HorizontalBarSection.test.tsx b/packages/components/src/internal/components/chart/HorizontalBarSection.test.tsx index 468bbb25c0..9bd7b4bf7c 100644 --- a/packages/components/src/internal/components/chart/HorizontalBarSection.test.tsx +++ b/packages/components/src/internal/components/chart/HorizontalBarSection.test.tsx @@ -91,4 +91,36 @@ describe('HorizontalBarSection', () => { expect(parts[2].getAttribute('class')).toContain('horizontal-bar--open'); expect(parts[2].parentElement.getAttribute('style')).toBe('width: 50%;'); }); + + // section headers offset the legend rows from the bars, so the hovered bar must be matched by barIndex + test('summary tooltip highlights the hovered bar across section headers', async () => { + const sectioned: HorizontalBarData[] = [ + { title: 'red', name: 'Red', sectionLabel: 'Blood', count: 12, totalCount: 40, percent: 30, filled: true }, + { title: 'blue', name: 'Blue', sectionLabel: 'Blood', count: 8, totalCount: 40, percent: 20, filled: true }, + { + title: 'plasma', + name: 'No Color', + sectionLabel: 'Plasma', + count: 20, + totalCount: 40, + percent: 50, + filled: true, + unlabeled: true, + }, + ]; + render(); + + // legend is: [Blood header, Red, Blue, Plasma] -- hovering the 2nd bar must bold the 3rd legend row + await userEvent.hover(document.querySelectorAll('.horizontal-bar-part')[1]); + await waitFor(() => { + expect(document.querySelector('.popover-content')).toBeInTheDocument(); + }); + + const labels = document.querySelectorAll('.popover-content .cell-legend-label'); + expect(labels).toHaveLength(3); + expect(document.querySelectorAll('.popover-content .cell-legend-section')).toHaveLength(1); + expect(labels[1]).toHaveTextContent('Blue'); + expect(labels[1].getAttribute('class')).toContain('bold-text'); + expect(document.querySelectorAll('.popover-content .bold-text')).toHaveLength(1); + }); }); diff --git a/packages/components/src/internal/components/chart/HorizontalBarSection.tsx b/packages/components/src/internal/components/chart/HorizontalBarSection.tsx index b3156ddd0f..954c03f3a6 100644 --- a/packages/components/src/internal/components/chart/HorizontalBarSection.tsx +++ b/packages/components/src/internal/components/chart/HorizontalBarSection.tsx @@ -46,8 +46,9 @@ export const HorizontalBarSection: FC = memo(props => { } horizontalBars = data - .filter(row => row.percent > 0) - .map((row, index) => { + .map((row, barIndex) => ({ row, barIndex })) + .filter(({ row }) => row.percent > 0) + .map(({ row, barIndex }) => { const style: CSSProperties = { width: row.percent + '%', background: row.backgroundColor }; const section = (
= memo(props => { const overlay = ( {showSummaryTooltip && summaryLegendData?.length > 0 ? ( - + // section headers shift legend rows out of step with the bars, so match on barIndex + legend.barIndex === barIndex)} + /> ) : ( row.title )} @@ -76,7 +81,7 @@ export const HorizontalBarSection: FC = memo(props => { ); return ( - + {section} ); diff --git a/packages/components/src/internal/components/chart/ItemsLegend.test.tsx b/packages/components/src/internal/components/chart/ItemsLegend.test.tsx index 6630c36d0b..bad3b6b5e5 100644 --- a/packages/components/src/internal/components/chart/ItemsLegend.test.tsx +++ b/packages/components/src/internal/components/chart/ItemsLegend.test.tsx @@ -192,4 +192,50 @@ describe('ItemsLegend', () => { expect(legends[1].querySelectorAll('.bold-text')).toHaveLength(1); expect(legends[2].querySelectorAll('a')).toHaveLength(0); }); + + test('section headers', () => { + render( + + ); + + const rows = document.querySelectorAll('tr'); + expect(rows).toHaveLength(4); + + // the header carries the section total but no color swatch, and is not bolded + expect(document.getElementsByClassName('cell-legend-section')).toHaveLength(1); + expect(rows[0]).toHaveTextContent('Blood'); + expect(rows[0]).toHaveTextContent('20'); + expect(rows[0].querySelectorAll('.cell-legend-icon')).toHaveLength(0); + expect(rows[0].querySelectorAll('.cell-legend-section-label')).toHaveLength(2); + expect(rows[0].getAttribute('class')).not.toContain('cell-legend-row--separator'); + + expect(rows[1].querySelectorAll('.cell-legend-circle')).toHaveLength(1); + expect(rows[1].querySelectorAll('.bold-text')).toHaveLength(0); + expect(rows[2].querySelectorAll('.bold-text')).toHaveLength(1); // activeIndex + expect(rows[3]).toHaveTextContent('Plasma'); + + // the rule closing off the Blood section sits on the row that follows it + expect(document.getElementsByClassName('cell-legend-row--separator')).toHaveLength(1); + expect(rows[3].getAttribute('class')).toContain('cell-legend-row--separator'); + }); }); diff --git a/packages/components/src/internal/components/chart/ItemsLegend.tsx b/packages/components/src/internal/components/chart/ItemsLegend.tsx index d8ee01952f..1c38e8c47d 100644 --- a/packages/components/src/internal/components/chart/ItemsLegend.tsx +++ b/packages/components/src/internal/components/chart/ItemsLegend.tsx @@ -25,6 +25,27 @@ export const ItemsLegend: FC = memo(props => { {legendData.map((legend, index) => { + const rowClassName = classNames('cell-legend-row', { + 'cell-legend-row--separator': legend.separatorAbove, + }); + + if (legend.isSectionHeader) { + return ( + + + {legend.data && ( + + )} + + ); + } + let icon; if (legend.circleColor && legend.circleColor !== 'none') { icon = ( @@ -55,7 +76,7 @@ export const ItemsLegend: FC = memo(props => { }); return ( - +
+ {legend.legendLabel} + + + + +
{icon} diff --git a/packages/components/src/internal/components/chart/utils.test.ts b/packages/components/src/internal/components/chart/utils.test.ts index 9f532aed81..f995e25ef1 100644 --- a/packages/components/src/internal/components/chart/utils.test.ts +++ b/packages/components/src/internal/components/chart/utils.test.ts @@ -220,6 +220,7 @@ describe('createHorizontalBarLegendData', () => { '#/freezers/test/storageView?query.SampleType/Name~eq=Sample Type 1' ), legendLabel: 'Sample Type 1', + barIndex: 0, }, { circleColor: 'blue', @@ -231,6 +232,7 @@ describe('createHorizontalBarLegendData', () => { '#/freezers/test/storageView?query.SampleType/Name~eq=Sample Type 1' ), legendLabel: 'Sample Type 1', + barIndex: 1, }, { circleColor: 'red', @@ -242,6 +244,7 @@ describe('createHorizontalBarLegendData', () => { '#/freezers/test/storageView?query.SampleType/Name~eq=Sample Type 2' ), legendLabel: 'Sample Type 2', + barIndex: 2, }, { circleColor: 'red', @@ -253,6 +256,7 @@ describe('createHorizontalBarLegendData', () => { '#/freezers/test/storageView?query.SampleType/Name~eq=Sample Type 3' ), legendLabel: 'Sample Type 3', + barIndex: 3, }, ]); }); @@ -296,15 +300,170 @@ describe('createHorizontalBarLegendData', () => { '#/freezers/test/storageView?query.SampleType/Name~eq=Sample Type 1' ), legendLabel: 'Sample Type 1', + barIndex: 0, }, { circleColor: 'fff', backgroundColor: 'none', data: Map.of('value', '6,000'), legendLabel: 'spaces', + barIndex: 1, }, ]); }); + + describe('sectionLabel', () => { + const bar = ( + name: string, + sectionLabel: string, + count: number, + backgroundColor: string, + unlabeled?: boolean + ) => ({ + title: `${count} '${sectionLabel}' samples (${name})`, + name, + sectionLabel, + count, + totalCount: 100, + percent: count, + backgroundColor, + filled: true, + unlabeled, + }); + + test('section with multiple bars gets a header', () => { + const legend = createHorizontalBarCountLegendData( + [bar('Red', 'Blood', 12, '#ff0000'), bar('Blue', 'Blood', 8, '#0000ff')], + 'space', + 'spaces' + ); + + expect(legend).toHaveLength(3); + expect(legend[0]).toStrictEqual({ + circleColor: 'none', + backgroundColor: 'none', + legendLabel: 'Blood', + isSectionHeader: true, + separatorAbove: false, // nothing above the first row to separate it from + data: Map.of('value', '20'), // the section total + }); + // the section label is not repeated in each row + expect(legend[1].legendLabel).toBe('Red'); + expect(legend[1].barIndex).toBe(0); + expect(legend[2].legendLabel).toBe('Blue'); + expect(legend[2].barIndex).toBe(1); + }); + + test('section with a single unlabeled bar renders as a plain row labeled by the section', () => { + const legend = createHorizontalBarCountLegendData( + [bar('No Color', 'Blood', 12, '#ff0000', true)], + 'space', + 'spaces' + ); + + expect(legend).toHaveLength(1); + expect(legend[0].isSectionHeader).toBeUndefined(); + expect(legend[0].legendLabel).toBe('Blood'); + expect(legend[0].circleColor).toBe('#ff0000'); + expect(legend[0].barIndex).toBe(0); + }); + + test('section with a single labeled bar keeps its header', () => { + const legend = createHorizontalBarCountLegendData([bar('Red', 'Blood', 12, '#ff0000')], 'space', 'spaces'); + + expect(legend).toHaveLength(2); + expect(legend[0]).toStrictEqual({ + circleColor: 'none', + backgroundColor: 'none', + legendLabel: 'Blood', + isSectionHeader: true, + separatorAbove: false, + data: Map.of('value', '12'), + }); + expect(legend[1].legendLabel).toBe('Red'); + expect(legend[1].circleColor).toBe('#ff0000'); + expect(legend[1].barIndex).toBe(0); + }); + + test('mixes sections, single-bar sections and unsectioned rows', () => { + const legend = createHorizontalBarCountLegendData( + [ + bar('Red', 'Blood', 12, '#ff0000'), + bar('Blue', 'Blood', 8, '#0000ff'), + bar('No Color', 'Plasma', 22, 'green', true), + { title: '4 spaces available', count: 4, totalCount: 100, percent: 4, filled: false }, + ], + 'space', + 'spaces' + ); + + expect(legend.map(l => l.legendLabel)).toStrictEqual(['Blood', 'Red', 'Blue', 'Plasma', 'spaces']); + expect(legend.map(l => l.barIndex)).toStrictEqual([undefined, 0, 1, 2, 3]); + // a rule closes off the Blood section; nothing separates the two unsectioned rows that follow + expect(legend.map(l => !!l.separatorAbove)).toStrictEqual([false, false, false, true, false]); + }); + + test('separators fence each section off from its neighbors', () => { + const legend = createHorizontalBarCountLegendData( + [ + { title: 'a', name: 'Other', count: 3, totalCount: 100, percent: 3, filled: true }, + bar('Red', 'Blood', 12, '#ff0000'), + bar('Blue', 'Blood', 8, '#0000ff'), + bar('Red', 'Plasma', 5, '#ff0000'), + bar('Green', 'Plasma', 4, 'green'), + ], + 'space', + 'spaces' + ); + + expect(legend.map(l => l.legendLabel)).toStrictEqual([ + 'Other', + 'Blood', + 'Red', + 'Blue', + 'Plasma', + 'Red', + 'Green', + ]); + // only the two section headers carry a rule -- the leading unsectioned row does not + expect(legend.map(l => !!l.separatorAbove)).toStrictEqual([ + false, + true, + false, + false, + true, + false, + false, + ]); + }); + + test('same section label split by another section is not merged', () => { + const legend = createHorizontalBarCountLegendData( + [ + bar('No Color', 'Blood', 12, '#ff0000', true), + bar('No Color', 'Plasma', 8, '#ff0000', true), + bar('No Color', 'Blood', 5, 'b', true), + ], + 'space', + 'spaces' + ); + + expect(legend.map(l => l.legendLabel)).toStrictEqual(['Blood', 'Plasma', 'Blood']); + expect(legend.every(l => !l.isSectionHeader)).toBe(true); + }); + + test('runs of the same section label are not merged across an intervening section', () => { + const legend = createHorizontalBarCountLegendData( + [bar('Red', 'Blood', 12, '#ff0000'), bar('Red', 'Plasma', 8, '#ff0000'), bar('Blue', 'Blood', 5, 'b')], + 'space', + 'spaces' + ); + + expect(legend.map(l => l.legendLabel)).toStrictEqual(['Blood', 'Red', 'Plasma', 'Red', 'Blood', 'Blue']); + expect(legend.map(l => !!l.isSectionHeader)).toStrictEqual([true, false, true, false, true, false]); + expect(legend.map(l => l.barIndex)).toStrictEqual([undefined, 0, undefined, 1, undefined, 2]); + }); + }); }); describe('getFieldDataType', () => { diff --git a/packages/components/src/internal/components/chart/utils.ts b/packages/components/src/internal/components/chart/utils.ts index a16a09bb24..9ec00788af 100644 --- a/packages/components/src/internal/components/chart/utils.ts +++ b/packages/components/src/internal/components/chart/utils.ts @@ -19,18 +19,23 @@ export interface HorizontalBarData { href?: string; name?: string; percent: number; + sectionLabel?: string; // groups consecutive bars under a shared heading in the summary legend title: string; totalCount: number; + unlabeled?: boolean; // name adds nothing beyond sectionLabel, so a section holding only this bar collapses to one row } export interface HorizontalBarLegendData { backgroundColor: string; + barIndex?: number; // index of the HorizontalBarData this entry came from; undefined for section headers borderColor?: string; circleColor: string; data?: Map; expired?: boolean; + isSectionHeader?: boolean; legendLabel: string; locked?: boolean; + separatorAbove?: boolean; // draws a rule above this row to fence off the section that ends before it } export function createHorizontalBarLegendData(data: HorizontalBarData[]): HorizontalBarLegendData[] { @@ -60,24 +65,59 @@ export function createHorizontalBarCountLegendData( emptyTextSingular: string, emptyTextPlural: string ): HorizontalBarLegendData[] { - return data - .filter(row => row.count > 0) - .reduce((legendData, row) => { - const countDisplay = row.count.toLocaleString(); - let legendLabel = row.name; - if (!row.filled) { - legendLabel = row.count > 1 ? emptyTextPlural : emptyTextSingular; - } + const bars = data.map((row, barIndex) => ({ row, barIndex })).filter(({ row }) => row.count > 0); + const legendData: HorizontalBarLegendData[] = []; + + const toEntry = (row: HorizontalBarData, barIndex: number, legendLabel: string): HorizontalBarLegendData => { + const countDisplay = row.count.toLocaleString(); + return { + circleColor: row.backgroundColor ?? 'fff', + backgroundColor: 'none', + legendLabel: row.filled ? legendLabel : row.count > 1 ? emptyTextPlural : emptyTextSingular, + barIndex, + data: row.href ? Map.of('value', countDisplay, 'url', row.href) : Map.of('value', countDisplay), + }; + }; + + let previousWasSectioned = false; + + for (let i = 0; i < bars.length; i++) { + const { row, barIndex } = bars[i]; + const { sectionLabel } = row; + let end = i; + if (sectionLabel) { + while (end + 1 < bars.length && bars[end + 1].row.sectionLabel === sectionLabel) end++; + } + + // a lone bar keeps its header unless it is unlabeled, where the header and row would read identically + const collapses = end === i && row.unlabeled; + + if (sectionLabel && !collapses) { + const section = bars.slice(i, end + 1); + const total = section.reduce((sum, bar) => sum + bar.row.count, 0); legendData.push({ - circleColor: row.backgroundColor ?? 'fff', + circleColor: 'none', backgroundColor: 'none', - legendLabel, - data: row.href ? Map.of('value', countDisplay, 'url', row.href) : Map.of('value', countDisplay), + legendLabel: sectionLabel, + isSectionHeader: true, + separatorAbove: legendData.length > 0, + data: Map.of('value', total.toLocaleString()), }); + section.forEach(bar => legendData.push(toEntry(bar.row, bar.barIndex, bar.row.name))); + previousWasSectioned = true; + } else { + const entry = toEntry(row, barIndex, sectionLabel ?? row.name); + // close off the preceding section so its rows don't read as part of this one + if (previousWasSectioned) entry.separatorAbove = true; + legendData.push(entry); + previousWasSectioned = false; + } - return legendData; - }, []); + i = end; + } + + return legendData; } export const getFieldDataType = (fieldData: Record): string => { diff --git a/packages/components/src/internal/components/domainproperties/SystemFields.test.tsx b/packages/components/src/internal/components/domainproperties/SystemFields.test.tsx index 997da415f9..7039bd0f4e 100644 --- a/packages/components/src/internal/components/domainproperties/SystemFields.test.tsx +++ b/packages/components/src/internal/components/domainproperties/SystemFields.test.tsx @@ -14,7 +14,7 @@ import { SystemFields } from './SystemFields'; describe('SystemFields', () => { function verifyEnableCheckbox(enableCheckboxes: any, isExpDateDisabled: boolean) { - expect(enableCheckboxes.length).toEqual(7 * 2); + expect(enableCheckboxes.length).toEqual(8 * 2); const nameCheckbox = enableCheckboxes[0]; expect(nameCheckbox.hasAttribute('checked')).toBeTruthy(); expect(nameCheckbox.hasAttribute('disabled')).toBeTruthy(); @@ -24,7 +24,10 @@ describe('SystemFields', () => { const descCheckbox = enableCheckboxes[4]; expect(descCheckbox.hasAttribute('checked')).toBeTruthy(); expect(descCheckbox.hasAttribute('disabled')).toBeFalsy(); - const expCheckbox = enableCheckboxes[6]; + const sampleColorCheckbox = enableCheckboxes[6]; + expect(sampleColorCheckbox.hasAttribute('checked')).toBeTruthy(); + expect(sampleColorCheckbox.hasAttribute('disabled')).toBeTruthy(); + const expCheckbox = enableCheckboxes[8]; expect(expCheckbox.hasAttribute('checked')).toEqual(!isExpDateDisabled); expect(expCheckbox.hasAttribute('disabled')).toBeFalsy(); } diff --git a/packages/components/src/internal/components/domainproperties/samples/SampleTypePropertiesPanel.tsx b/packages/components/src/internal/components/domainproperties/samples/SampleTypePropertiesPanel.tsx index e06920d390..b1b6249b59 100644 --- a/packages/components/src/internal/components/domainproperties/samples/SampleTypePropertiesPanel.tsx +++ b/packages/components/src/internal/components/domainproperties/samples/SampleTypePropertiesPanel.tsx @@ -32,7 +32,7 @@ import { ENTITY_FORM_IDS } from '../entities/constants'; import { AutoLinkToStudyDropdown } from '../AutoLinkToStudyDropdown'; import { isSampleManagerEnabled } from '../../../app/products'; -import { getCurrentProductName, isCommunityDistribution, isSampleColorsEnabled } from '../../../app/utils'; +import { getCurrentProductName, isCommunityDistribution } from '../../../app/utils'; import { PREFIX_SUBSTITUTION_EXPRESSION, PROPERTIES_PANEL_NAMING_PATTERN_WARNING_MSG } from '../constants'; @@ -618,9 +618,7 @@ class SampleTypePropertiesPanelImpl extends PureComponent - {isSampleColorsEnabled() && ( - - )} + {includeMetricUnitProperty && ( <>
diff --git a/packages/components/src/internal/components/domainproperties/samples/__snapshots__/SampleTypePropertiesPanel.test.tsx.snap b/packages/components/src/internal/components/domainproperties/samples/__snapshots__/SampleTypePropertiesPanel.test.tsx.snap index d577011f50..0d7e2c2cdb 100644 --- a/packages/components/src/internal/components/domainproperties/samples/__snapshots__/SampleTypePropertiesPanel.test.tsx.snap +++ b/packages/components/src/internal/components/domainproperties/samples/__snapshots__/SampleTypePropertiesPanel.test.tsx.snap @@ -260,6 +260,47 @@ exports[`SampleTypePropertiesPanel appPropertiesOnly 1`] = `
+
+
+ + Sample + + Colors +
+ + + +
+
+
+
+
+ +