From 163345ed0892ac3eff4ad720a350c2be30cc7586 Mon Sep 17 00:00:00 2001 From: Tamas Kovacs Date: Wed, 17 Dec 2025 15:30:25 +0100 Subject: [PATCH] feat(ui-color-picker): add new labelLevel prop to set heading level in ColorContrast INSTUI-4874 --- packages/ui-color-picker/package.json | 1 + .../__tests__/ColorContrast.test.tsx | 49 ++++++++++++++++++- .../src/ColorContrast/index.tsx | 14 ++++-- .../src/ColorContrast/props.ts | 8 +++ .../src/ColorContrast/styles.ts | 9 +++- packages/ui-color-picker/tsconfig.build.json | 1 + pnpm-lock.yaml | 3 ++ 7 files changed, 79 insertions(+), 6 deletions(-) diff --git a/packages/ui-color-picker/package.json b/packages/ui-color-picker/package.json index f60f892fee..eac859e283 100644 --- a/packages/ui-color-picker/package.json +++ b/packages/ui-color-picker/package.json @@ -33,6 +33,7 @@ "@instructure/ui-dom-utils": "workspace:*", "@instructure/ui-drilldown": "workspace:*", "@instructure/ui-form-field": "workspace:*", + "@instructure/ui-heading": "workspace:*", "@instructure/ui-icons": "workspace:*", "@instructure/ui-pill": "workspace:*", "@instructure/ui-popover": "workspace:*", diff --git a/packages/ui-color-picker/src/ColorContrast/__tests__/ColorContrast.test.tsx b/packages/ui-color-picker/src/ColorContrast/__tests__/ColorContrast.test.tsx index f982112c2b..d653e21dda 100644 --- a/packages/ui-color-picker/src/ColorContrast/__tests__/ColorContrast.test.tsx +++ b/packages/ui-color-picker/src/ColorContrast/__tests__/ColorContrast.test.tsx @@ -22,7 +22,7 @@ * SOFTWARE. */ -import { render } from '@testing-library/react' +import { render, screen } from '@testing-library/react' import { vi } from 'vitest' import '@testing-library/jest-dom' import { runAxeCheck } from '@instructure/ui-axe-check' @@ -76,6 +76,53 @@ describe('', () => { }) }) + describe('labelLevel prop', () => { + it('should render label as div when labelLevel is not provided', async () => { + render() + + const heading = screen.queryByRole('heading', { + name: testLabels.label + }) + expect(heading).not.toBeInTheDocument() + + const labelText = screen.getByText(testLabels.label) + expect(labelText).toBeInTheDocument() + expect(labelText.tagName.toLowerCase()).toBe('div') + }) + + it('should render label as Heading when labelLevel is provided', async () => { + render() + + const heading = screen.getByRole('heading', { + name: testLabels.label, + level: 2 + }) + expect(heading).toBeInTheDocument() + }) + + it('should render correct heading level', async () => { + const { rerender } = render( + + ) + + let heading = screen.getByRole('heading', { + name: testLabels.label, + level: 3 + }) + expect(heading).toBeInTheDocument() + + rerender( + + ) + + heading = screen.getByRole('heading', { + name: testLabels.label, + level: 1 + }) + expect(heading).toBeInTheDocument() + }) + }) + describe('should calculate contrast correctly', () => { it('on opaque colors', async () => { const color1 = '#fff' diff --git a/packages/ui-color-picker/src/ColorContrast/index.tsx b/packages/ui-color-picker/src/ColorContrast/index.tsx index 790534f736..962ca4d334 100644 --- a/packages/ui-color-picker/src/ColorContrast/index.tsx +++ b/packages/ui-color-picker/src/ColorContrast/index.tsx @@ -33,6 +33,7 @@ import { import { withStyle } from '@instructure/emotion' import { Text } from '@instructure/ui-text' +import { Heading } from '@instructure/ui-heading' import { Pill } from '@instructure/ui-pill' import ColorIndicator from '../ColorIndicator' @@ -191,6 +192,7 @@ class ColorContrast extends Component { const { styles, label, + labelLevel, normalTextLabel, largeTextLabel, graphicsTextLabel @@ -211,9 +213,15 @@ class ColorContrast extends Component { data-cid="ColorContrast" >
- - {label} - + {labelLevel ? ( + + {label} + + ) : ( + + {label} + + )}
{contrast}:1 {this.renderPreview()} diff --git a/packages/ui-color-picker/src/ColorContrast/props.ts b/packages/ui-color-picker/src/ColorContrast/props.ts index b5b0163420..c8abd09b60 100644 --- a/packages/ui-color-picker/src/ColorContrast/props.ts +++ b/packages/ui-color-picker/src/ColorContrast/props.ts @@ -22,12 +22,15 @@ * SOFTWARE. */ +import React from 'react' import type { WithStyleProps, ComponentStyle } from '@instructure/emotion' import type { OtherHTMLAttributes, ColorContrastTheme } from '@instructure/shared-types' +type HeadingLevel = U + type ColorContrastOwnProps = { /** * Provides a reference to the component's underlying html element. @@ -53,6 +56,10 @@ type ColorContrastOwnProps = { * Label of the component */ label: string + /** + * The heading level for the label. If provided, the label will be rendered as a `` instead of ``. + */ + labelLevel?: HeadingLevel<'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'> /** * Text of the second check (Suggested english text: Large text) */ @@ -154,6 +161,7 @@ const allowedProps: AllowedPropKeys = [ 'graphicsTextLabel', 'withoutColorPreview', 'label', + 'labelLevel', 'largeTextLabel', 'normalTextLabel', 'secondColor', diff --git a/packages/ui-color-picker/src/ColorContrast/styles.ts b/packages/ui-color-picker/src/ColorContrast/styles.ts index a9db350f07..d2dac4bd10 100644 --- a/packages/ui-color-picker/src/ColorContrast/styles.ts +++ b/packages/ui-color-picker/src/ColorContrast/styles.ts @@ -23,6 +23,7 @@ */ import type { ColorContrastTheme } from '@instructure/shared-types' +import type { ColorContrastProps } from './props' /** * --- * private: true @@ -33,7 +34,10 @@ import type { ColorContrastTheme } from '@instructure/shared-types' * @param {Object} state the state of the component, the style is applied to * @return {Object} The final style object, which will be used in the component */ -const generateStyle = (componentTheme: ColorContrastTheme) => { +const generateStyle = ( + componentTheme: ColorContrastTheme, + props: ColorContrastProps +) => { const statusDescriptionStyle = (pass: boolean) => ({ label: pass ? 'colorContrast__successDescription' @@ -94,7 +98,8 @@ const generateStyle = (componentTheme: ColorContrastTheme) => { }, label: { label: 'colorContrast__label', - marginBottom: componentTheme.labelBottomMargin + marginBottom: componentTheme.labelBottomMargin, + ...(props.labelLevel && { fontWeight: 'bold' }) } } } diff --git a/packages/ui-color-picker/tsconfig.build.json b/packages/ui-color-picker/tsconfig.build.json index 81bd533281..299f914361 100644 --- a/packages/ui-color-picker/tsconfig.build.json +++ b/packages/ui-color-picker/tsconfig.build.json @@ -18,6 +18,7 @@ { "path": "../ui-buttons/tsconfig.build.json" }, { "path": "../ui-color-utils/tsconfig.build.json" }, { "path": "../ui-form-field/tsconfig.build.json" }, + { "path": "../ui-heading/tsconfig.build.json" }, { "path": "../ui-icons/tsconfig.build.json" }, { "path": "../ui-pill/tsconfig.build.json" }, { "path": "../ui-popover/tsconfig.build.json" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 231714ddeb..f0d7da0c94 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1579,6 +1579,9 @@ importers: '@instructure/ui-form-field': specifier: workspace:* version: link:../ui-form-field + '@instructure/ui-heading': + specifier: workspace:* + version: link:../ui-heading '@instructure/ui-icons': specifier: workspace:* version: link:../ui-icons