diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index b7c0255..9b77184 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -9,14 +9,13 @@ jobs:
publish:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16.x'
registry-url: 'https://npm.pkg.github.com'
scope: '@ecmwf-projects'
- run: yarn install --frozen-lockfile
- - run: yarn test
- run: yarn build
- run: yarn publish
env:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 3a2f4cb..2636b73 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -6,7 +6,7 @@ jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16.x'
@@ -17,8 +17,8 @@ jobs:
component-test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Cypress run
- uses: cypress-io/github-action@v4
+ uses: cypress-io/github-action@v5
with:
command: yarn test:components:ci
diff --git a/.gitignore b/.gitignore
index 6704566..7d58996 100644
--- a/.gitignore
+++ b/.gitignore
@@ -102,3 +102,6 @@ dist
# TernJS port file
.tern-port
+
+# Cypress
+cypress/videos
diff --git a/.nycrc.json b/.nycrc.json
new file mode 100644
index 0000000..2b5c8ca
--- /dev/null
+++ b/.nycrc.json
@@ -0,0 +1,7 @@
+{
+ "all": true,
+ "extends": "@istanbuljs/nyc-config-typescript",
+ "check-coverage": true,
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
+ "exclude": ["cypress/**/*.*", "**/*.d.ts", "**/*.cy.tsx", "**/*.cy.ts"]
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3848a91..d88713c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [5.1.0] 2023-04-21
+
+## Added
+
+- ability to bypass the `required` attribute for `StringListArrayWidget`, `StringListWidget`, `StringChoiceWidget`, and `ExclusiveGroupWidget` children.
+
## [5.0.2] - 2023-04-18
## Fixed
diff --git a/__tests__/factories.ts b/__tests__/factories.ts
index 9ea4378..a20d827 100644
--- a/__tests__/factories.ts
+++ b/__tests__/factories.ts
@@ -55,7 +55,7 @@ export const getStringListArrayWidgetConfiguration = () => {
],
id: 1
},
- help: 'Please, consult the product user guide in the documentation section for more information on these variables.',
+ help: null,
label: 'Variable',
name: 'variable',
required: true,
@@ -119,7 +119,7 @@ export const getStringListWidgetConfiguration = () => {
'monthly_averaged_reanalysis_by_hour_of_day'
]
},
- help: 'Monthly averaged reanalysis data are produced by averaging all daily data. Monthly averages by hour of day constitute the average over all data within the calendar month for every hour (UTC) of the day.',
+ help: null,
label: 'Product type',
name: 'product_type',
required: true,
diff --git a/cypress.config.ts b/cypress.config.ts
index 089a80c..e5bbb0a 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -1,10 +1,20 @@
import { defineConfig } from 'cypress'
export default defineConfig({
+ env: {
+ codeCoverage: { exclude: 'cypress/**/*.*' }
+ },
+ video: false,
component: {
devServer: {
framework: 'react',
bundler: 'vite'
+ },
+ screenshotOnRunFailure: true,
+ setupNodeEvents(on, config) {
+ require('@cypress/code-coverage/task')(on, config)
+
+ return config
}
}
})
diff --git a/cypress/component/ExclusiveGroupWidget.cy.tsx b/cypress/component/ExclusiveGroupWidget.cy.tsx
index 092bbc4..106b834 100644
--- a/cypress/component/ExclusiveGroupWidget.cy.tsx
+++ b/cypress/component/ExclusiveGroupWidget.cy.tsx
@@ -37,7 +37,7 @@ describe('', () => {
const configuration = {
type: 'ExclusiveGroupWidget' as const,
label: 'Generic selections',
- help: 'Select one choice from the widgets below',
+ help: null,
name: 'checkbox_groups',
children: ['variable', 'surface_help'],
details: {
@@ -55,17 +55,15 @@ describe('', () => {
cy.viewport(800, 600)
cy.mount(
-
-
-
+
).then(({ rerender }) => {
cy.findByLabelText('Lake shape factor').click()
cy.findByLabelText('Soil temperature level 3').click()
@@ -108,7 +106,7 @@ describe('', () => {
const configuration = {
type: 'ExclusiveGroupWidget' as const,
label: 'Generic selections',
- help: 'Select one choice from the widgets below',
+ help: null,
name: 'checkbox_groups',
children: ['product_type', 'surface_help'],
details: {
@@ -136,17 +134,15 @@ describe('', () => {
cy.viewport(984, 597)
cy.mount(
-
-
-
+
)
cy.findByLabelText('Monthly averaged reanalysis').click()
@@ -163,7 +159,7 @@ describe('', () => {
const configuration = {
type: 'ExclusiveGroupWidget' as const,
label: 'Generic selections',
- help: 'Select one choice from the widgets below',
+ help: null,
name: 'checkbox_groups',
children: ['product_type', 'variable'],
details: {
@@ -178,15 +174,13 @@ describe('', () => {
]
cy.mount(
-
-
-
+
)
cy.findByLabelText('Skin temperature')
@@ -196,7 +190,7 @@ describe('', () => {
const configuration = {
type: 'ExclusiveGroupWidget' as const,
label: 'Geographical area',
- help: 'Select one choice from the widgets below',
+ help: null,
name: 'area_group',
children: ['global', 'area'],
details: {
@@ -217,6 +211,7 @@ describe('', () => {
},
{
...getGeographicExtentWidgetConfiguration(),
+ help: null,
label: 'Sub-region extraction'
}
]
@@ -225,17 +220,15 @@ describe('', () => {
cy.viewport(1200, 900)
cy.mount(
-
-
-
+
)
cy.findByLabelText('Sub-region extraction').should(
@@ -267,7 +260,7 @@ describe('', () => {
const configuration = {
type: 'ExclusiveGroupWidget' as const,
label: 'Generic selections',
- help: 'Select one choice from the widgets below',
+ help: null,
name: 'checkbox_groups',
children: ['format', 'surface_help'],
details: {
@@ -285,17 +278,15 @@ describe('', () => {
cy.viewport(800, 600)
cy.mount(
-
-
-
+
)
cy.findByLabelText('NetCDF (experimental)').click()
@@ -311,7 +302,7 @@ describe('', () => {
const thisExclusive = {
type: 'ExclusiveGroupWidget' as const,
label: 'This exclusive',
- help: 'This exclusive',
+ help: null,
name: 'this_exclusive',
children: ['format', 'surface_help'],
details: {
@@ -322,7 +313,7 @@ describe('', () => {
const otherExclusive = {
type: 'ExclusiveGroupWidget' as const,
label: 'Other exclusive',
- help: 'Other exclusive',
+ help: null,
name: 'other_exclusive',
children: ['product_type'],
details: {
@@ -368,25 +359,23 @@ describe('', () => {
cy.viewport(800, 600)
cy.mount(
-
-
-
+
)
cy.findByLabelText(/netcdf/i)
@@ -400,4 +389,48 @@ describe('', () => {
['product_type', 'monthly_averaged_reanalysis']
])
})
+
+ it('bypasses the required attribute if all options are made unavailable by constraints', () => {
+ const configuration = {
+ type: 'ExclusiveGroupWidget' as const,
+ label: 'Generic selections',
+ help: null,
+ name: 'checkbox_groups',
+ children: ['product_type', 'variable', 'format'],
+ details: {
+ default: 'product_type'
+ }
+ }
+
+ const formConfiguration = [
+ configuration,
+ getStringListWidgetConfiguration(),
+ getStringListArrayWidgetConfiguration(),
+ getStringChoiceWidgetConfiguration()
+ ]
+
+ const stubbedHandleSubmit = cy.stub().as('stubbedHandleSubmit')
+
+ cy.mount(
+
+ )
+
+ cy.findByText('submit').click()
+
+ cy.get('@stubbedHandleSubmit').should('have.been.calledOnceWith', [
+ ['bypassRequired', 'product_type'],
+ ['bypassRequired', 'variable'],
+ ['bypassRequired', 'format']
+ ])
+ })
})
diff --git a/cypress/component/StringChoiceWidget.cy.tsx b/cypress/component/StringChoiceWidget.cy.tsx
new file mode 100644
index 0000000..2cafff5
--- /dev/null
+++ b/cypress/component/StringChoiceWidget.cy.tsx
@@ -0,0 +1,67 @@
+import React from 'react'
+
+import { StringChoiceWidget } from '../../src'
+
+import { getStringChoiceWidgetConfiguration } from '../../__tests__/factories'
+
+const Form = ({
+ children,
+ handleSubmit
+}: {
+ children: React.ReactNode
+ handleSubmit: (...args: any) => void
+}) => {
+ return (
+
+ )
+}
+
+describe('', () => {
+ it('handles selection', () => {
+ const stubbedHandleSubmit = cy.stub().as('stubbedHandleSubmit')
+
+ cy.mount(
+
+ )
+
+ cy.findByLabelText('NetCDF (experimental)').click()
+ cy.findByText('submit').click()
+
+ cy.get('@stubbedHandleSubmit').should('have.been.calledOnceWith', [
+ ['format', 'netcdf']
+ ])
+ })
+
+ it('bypasses the required attribute if all options are made unavailable by constraints', () => {
+ const stubbedHandleSubmit = cy.stub().as('stubbedHandleSubmit')
+
+ cy.mount(
+
+ )
+
+ cy.findByText('submit').click()
+
+ cy.get('@stubbedHandleSubmit').should('have.been.calledOnceWith', [
+ ['bypassRequired', 'format']
+ ])
+ })
+})
diff --git a/cypress/component/StringListArrayWidget.cy.tsx b/cypress/component/StringListArrayWidget.cy.tsx
new file mode 100644
index 0000000..4f74088
--- /dev/null
+++ b/cypress/component/StringListArrayWidget.cy.tsx
@@ -0,0 +1,92 @@
+import React from 'react'
+
+import { StringListArrayWidget } from '../../src'
+
+import { getStringListArrayWidgetConfiguration } from '../../__tests__/factories'
+
+const Form = ({
+ children,
+ handleSubmit
+}: {
+ children: React.ReactNode
+ handleSubmit: (...args: any) => void
+}) => {
+ return (
+
+ )
+}
+
+describe('', () => {
+ it('appends current selection for closed accordions', () => {
+ const stubbedHandleSubmit = cy.stub().as('stubbedHandleSubmit')
+
+ cy.mount(
+
+ )
+
+ cy.findByText(/select all/i).click()
+
+ cy.findByText('submit').click()
+
+ cy.get('@stubbedHandleSubmit').should('have.been.calledOnceWith', [
+ ['variable', 'lake_bottom_temperature'],
+ ['variable', 'lake_ice_depth'],
+ ['variable', 'lake_ice_temperature'],
+ ['variable', 'lake_mix_layer_depth'],
+ ['variable', 'lake_mix_layer_temperature'],
+ ['variable', 'lake_shape_factor'],
+ ['variable', 'lake_total_layer_temperature'],
+ ['variable', '2m_dewpoint_temperature'],
+ ['variable', '2m_temperature'],
+ ['variable', 'skin_temperature'],
+ ['variable', 'soil_temperature_level_1'],
+ ['variable', 'soil_temperature_level_2'],
+ ['variable', 'soil_temperature_level_3'],
+ ['variable', 'soil_temperature_level_4']
+ ])
+ })
+
+ it('bypasses the required attribute if all options are made unavailable by constraints', () => {
+ const stubbedHandleSubmit = cy.stub().as('stubbedHandleSubmit')
+
+ cy.mount(
+
+ )
+
+ cy.findByText('submit').click()
+
+ cy.get('@stubbedHandleSubmit').should('have.been.calledOnceWith', [
+ ['bypassRequired', 'variable']
+ ])
+
+ cy.findByText(/at least one selection must be made/i).should('not.exist')
+ })
+})
diff --git a/cypress/component/StringListWidget.cy.tsx b/cypress/component/StringListWidget.cy.tsx
new file mode 100644
index 0000000..e60ee27
--- /dev/null
+++ b/cypress/component/StringListWidget.cy.tsx
@@ -0,0 +1,68 @@
+import React from 'react'
+
+import { StringListWidget } from '../../src'
+
+import { getStringListWidgetConfiguration } from '../../__tests__/factories'
+
+const Form = ({
+ children,
+ handleSubmit
+}: {
+ children: React.ReactNode
+ handleSubmit: (...args: any) => void
+}) => {
+ return (
+
+ )
+}
+
+describe('', () => {
+ it('handles selection', () => {
+ const stubbedHandleSubmit = cy.stub().as('stubbedHandleSubmit')
+
+ cy.mount(
+
+ )
+
+ cy.findByLabelText('Monthly averaged reanalysis').click()
+
+ cy.findByText('submit').click()
+
+ cy.get('@stubbedHandleSubmit').should('have.been.calledOnceWith', [
+ ['product_type', 'monthly_averaged_reanalysis']
+ ])
+ })
+
+ it('bypasses the required attribute if all options are made unavailable by constraints', () => {
+ const stubbedHandleSubmit = cy.stub().as('stubbedHandleSubmit')
+
+ cy.mount(
+
+ )
+
+ cy.findByText('submit').click()
+
+ cy.get('@stubbedHandleSubmit').should('have.been.calledOnceWith', [
+ ['bypassRequired', 'product_type']
+ ])
+
+ cy.findByText(/at least one selection must be made/i).should('not.exist')
+ })
+})
diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
index bc98b46..9586027 100644
--- a/cypress/support/commands.ts
+++ b/cypress/support/commands.ts
@@ -36,3 +36,4 @@
// }
// }
import '@testing-library/cypress/add-commands'
+import '@cypress/code-coverage/support'
diff --git a/package.json b/package.json
index a53e923..ad4dec0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@ecmwf-projects/cads-ui-library",
- "version": "5.0.2",
+ "version": "5.0.3-0",
"description": "Common UI kit library",
"repository": {
"type": "git",
@@ -24,10 +24,11 @@
"clean": "rimraf dist",
"prebuild": "npm run clean",
"build": "tsc",
- "test": "jest --coverage",
+ "test": "jest",
"test:watch": "jest --watch",
"test:components": "cypress open",
"test:components:ci": "cypress run --component",
+ "test:components:coverage": "nyc report",
"format": "prettier --write .",
"lint:format": "prettier --check .",
"lint": "eslint src/**",
@@ -37,38 +38,41 @@
"postversion": "git push --follow-tags"
},
"devDependencies": {
+ "@cypress/code-coverage": "^3.10.4",
+ "@istanbuljs/nyc-config-typescript": "^1.0.2",
"@testing-library/cypress": "^9.0.0",
"@testing-library/dom": "^9.2.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
- "@types/node": "^18.15.11",
- "@types/react": "^18.0.33",
+ "@types/node": "^18.15.12",
+ "@types/react": "^18.0.37",
"@types/react-dom": "^18.0.11",
"@types/styled-components": "^5.1.26",
"@types/styled-system": "^5.1.16",
"@types/testing-library__jest-dom": "^5.14.5",
- "@typescript-eslint/eslint-plugin": "^5.57.1",
- "@typescript-eslint/parser": "^5.57.1",
+ "@typescript-eslint/eslint-plugin": "^5.59.0",
+ "@typescript-eslint/parser": "^5.59.0",
"@vitejs/plugin-react": "^3.1.0",
"cypress": "^12.10.0",
- "eslint": "^8.37.0",
+ "eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.32.2",
- "husky": "^7.0.0",
+ "husky": "^8.0.3",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
- "lint-staged": "^13.0.3",
- "prettier": "^2.7.1",
- "rimraf": "^3.0.2",
+ "lint-staged": "^13.2.1",
+ "nyc": "^15.1.0",
+ "prettier": "^2.8.7",
+ "rimraf": "^5.0.0",
"styled-components": "^5.3.9",
"ts-jest": "^29.1.0",
- "ts-node": "^10.9.1",
- "type-fest": "^3.7.2",
- "typescript": "^5.0.3",
- "vite": "^4.2.1"
+ "type-fest": "^3.8.0",
+ "typescript": "^5.0.4",
+ "vite": "^4.3.0",
+ "vite-plugin-istanbul": "^4.0.1"
},
"dependencies": {
"@radix-ui/react-accordion": "^1.1.1",
@@ -90,5 +94,8 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"styled-components": "^5.3.5"
+ },
+ "resolutions": {
+ "json5": "2.2.3"
}
}
diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts
index 68aeb91..7a09943 100644
--- a/src/utils/hooks.ts
+++ b/src/utils/hooks.ts
@@ -1,5 +1,5 @@
-import { useState, useRef, useEffect } from 'react'
-import { useReadLocalStorage } from 'usehooks-ts'
+import { useState, useRef, useEffect, RefObject } from 'react'
+import { useReadLocalStorage, useEventListener } from 'usehooks-ts'
const useWidgetSelection = (fieldset: string) => {
const [selection, setSelection] = useState>({
@@ -38,4 +38,37 @@ const useWidgetSelection = (fieldset: string) => {
return { selection, setSelection }
}
-export { useWidgetSelection }
+type UseBypassRequired = (
+ elementRef: RefObject,
+ bypass?: boolean,
+ constraints?: string[]
+) => boolean
+/**
+ * Bypass the required attribute if all options are made unavailable by constraints.
+ */
+const useBypassRequired: UseBypassRequired = (
+ elementRef,
+ bypass = false,
+ constraints
+) => {
+ const form = useRef(elementRef.current?.form || null)
+
+ const injectBypassRequired = (ev: FormDataEvent) => {
+ if (!bypass) return
+ if (!elementRef.current) return
+ if (!elementRef.current.name) return
+ if (!constraints || constraints?.length) return
+
+ const { formData } = ev
+
+ formData.append('bypassRequired', elementRef.current.name)
+ }
+
+ useEventListener('formdata', injectBypassRequired, form)
+
+ if (!constraints) return false
+
+ return !constraints.length && bypass
+}
+
+export { useWidgetSelection, useBypassRequired }
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 77482fa..8f58a90 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -4,6 +4,6 @@ export {
isAllSelected
} from './constraints'
-export { useWidgetSelection } from './hooks'
+export { useWidgetSelection, useBypassRequired } from './hooks'
export { SelectAll, ClearAll } from './bulkSelector'
diff --git a/src/utils/widgetFactory.tsx b/src/utils/widgetFactory.tsx
index cb32b3e..896c23d 100644
--- a/src/utils/widgetFactory.tsx
+++ b/src/utils/widgetFactory.tsx
@@ -14,9 +14,15 @@ import type { FormConfiguration } from '../types/Form'
*/
type CreateWidget = (
configuration: FormConfiguration,
- constraints?: string[]
+ constraints?: string[],
+ opts?: {
+ /**
+ * When true, bypass the required attribute if all options are made unavailable by constraints.
+ */
+ bypassRequiredForConstraints?: boolean
+ }
) => (...props: any) => JSX.Element | null
-const createWidget: CreateWidget = (configuration, constraints) => {
+const createWidget: CreateWidget = (configuration, constraints, opts) => {
switch (configuration.type) {
case 'GeographicExtentWidget':
// eslint-disable-next-line react/display-name
@@ -28,6 +34,7 @@ const createWidget: CreateWidget = (configuration, constraints) => {
return props => (
@@ -37,6 +44,7 @@ const createWidget: CreateWidget = (configuration, constraints) => {
return props => (
@@ -46,6 +54,7 @@ const createWidget: CreateWidget = (configuration, constraints) => {
return props => (
diff --git a/src/widgets/ExclusiveGroupWidget.tsx b/src/widgets/ExclusiveGroupWidget.tsx
index 91b1d8d..dfaa7b7 100644
--- a/src/widgets/ExclusiveGroupWidget.tsx
+++ b/src/widgets/ExclusiveGroupWidget.tsx
@@ -98,12 +98,19 @@ const ExclusiveGroupWidget = ({
type GetExclusiveGroupChildren = (
formConfiguration: FormConfiguration[],
name: string,
- constraints?: Record
+ constraints?: Record,
+ opts?: {
+ /**
+ * When true, bypass the required attribute if all options are made unavailable by constraints.
+ */
+ bypassRequiredForConstraints?: boolean
+ }
) => ChildrenGetter
const getExclusiveGroupChildren: GetExclusiveGroupChildren = (
formConfiguration,
name,
- constraints
+ constraints,
+ opts
) => {
const thisExclusiveGroup = formConfiguration.find(
configuration =>
@@ -125,7 +132,11 @@ const getExclusiveGroupChildren: GetExclusiveGroupChildren = (
if (!childConfiguration) return childMap
const childConstraints = constraints && constraints[childName]
- const childWidget = createWidget(childConfiguration, childConstraints)
+ const childWidget = createWidget(
+ childConfiguration,
+ childConstraints,
+ opts
+ )
childMap[childName] = props => childWidget(props)
diff --git a/src/widgets/GeographicExtentWidget.tsx b/src/widgets/GeographicExtentWidget.tsx
index ae11cc0..c7dcc4a 100644
--- a/src/widgets/GeographicExtentWidget.tsx
+++ b/src/widgets/GeographicExtentWidget.tsx
@@ -60,8 +60,6 @@ const GeographicExtentWidget = ({
fieldsetDisabled,
labelAriaHidden = true
}: GeographicExtentWidgetProps) => {
- const fieldSetRef = useRef(null)
-
const injectWidgetPayload = (ev: FormDataEvent) => {
const { formData } = ev
/**
@@ -78,7 +76,7 @@ const GeographicExtentWidget = ({
}
}
- useEventListener('formdata', injectWidgetPayload, fieldSetRef)
+ useEventListener('formdata', injectWidgetPayload)
if (!configuration) return null
diff --git a/src/widgets/StringChoiceWidget.tsx b/src/widgets/StringChoiceWidget.tsx
index 17edc27..bac873d 100644
--- a/src/widgets/StringChoiceWidget.tsx
+++ b/src/widgets/StringChoiceWidget.tsx
@@ -21,7 +21,7 @@ import {
WidgetTitle
} from './Widget'
-import { isDisabled } from '../utils'
+import { isDisabled, useBypassRequired } from '../utils'
export interface StringChoiceWidgetDetails {
columns: number
@@ -56,14 +56,20 @@ interface StringChoiceWidgetProps {
* Whether to hide the widget label from ARIA.
*/
labelAriaHidden?: boolean
+ /**
+ * When true, bypass the required attribute if all options are made unavailable by constraints.
+ */
+ bypassRequiredForConstraints?: boolean
}
const StringChoiceWidget = ({
configuration,
constraints,
fieldsetDisabled,
- labelAriaHidden = true
+ labelAriaHidden = true,
+ bypassRequiredForConstraints
}: StringChoiceWidgetProps) => {
+ const fieldSetRef = useRef(null)
const [selection, setSelection] = useState([])
const persistedSelection = useReadLocalStorage<{
dataset: { id: string }
@@ -74,6 +80,8 @@ const StringChoiceWidget = ({
*/
const persistedSelectionRef = useRef(persistedSelection)
+ useBypassRequired(fieldSetRef, bypassRequiredForConstraints, constraints)
+
const { details, label, help, name } = configuration
const {
details: { columns }
@@ -122,7 +130,7 @@ const StringChoiceWidget = ({
triggerAriaLabel={`Get help for ${label}`}
/>
-