@openg2p/registry-widgets 1.1.0-dev.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -246,24 +246,27 @@ const getValidationPattern = (validationType) => {
246
246
  };
247
247
 
248
248
  /**
249
- * Validate value against validation rules
249
+ * Validate value against validation rules.
250
+ *
251
+ * @param skipRequired - When true, required-field checks are skipped (used by
252
+ * per-section Save/Next buttons so the user can move between sections without
253
+ * filling every mandatory field; only format/range checks still run).
250
254
  */
251
- const validateWidget = (value, validation, required = false) => {
255
+ const validateWidget = (value, validation, required = false, skipRequired = false) => {
252
256
  const errors = [];
253
257
  if (!validation && !required) {
254
258
  return errors;
255
259
  }
256
- // Check required
257
- const isRequired = validation?.required ?? required;
260
+ // Check required (skipped when navigating between sections)
261
+ const isRequired = !skipRequired && (validation?.required ?? required);
258
262
  // For boolean, false is a valid value, so only check for null/undefined/empty string
259
263
  const isEmpty = value === null || value === undefined || value === '';
260
264
  if (isRequired && isEmpty) {
261
265
  errors.push('This field is required');
262
266
  return errors; // Return early if required field is empty
263
267
  }
264
- // Skip other validations if value is empty and not required
265
- // Note: For boolean, false is a valid value, so we only skip if truly empty
266
- if (isEmpty && !isRequired) {
268
+ // Skip format/range validations if value is empty
269
+ if (isEmpty) {
267
270
  return errors;
268
271
  }
269
272
  if (!validation) {
@@ -1075,16 +1078,12 @@ const getApiDataSource = async (dataSource, allValues, dataSourceRequestHandler,
1075
1078
  console.error('[getApiDataSource] API data source missing endpoint. Use "endpoint" field to specify the operation (e.g., "get_g2p_geo_level_values")');
1076
1079
  return [];
1077
1080
  }
1078
- let response;
1079
- try {
1080
- response = await dataSourceRequestHandler(service, endpoint, method, requestParams, {
1081
- headers: dataSource.headers,
1082
- });
1083
- }
1084
- catch (error) {
1085
- console.error('[getApiDataSource] Handler error:', error);
1086
- throw error;
1087
- }
1081
+ // Call handler — let any throw propagate to the outer catch so it is logged once
1082
+ // by useBaseWidget rather than double-logged here (which can cascade when
1083
+ // intercept-console-error.js converts console.error calls into thrown errors).
1084
+ const response = await dataSourceRequestHandler(service, endpoint, method, requestParams, {
1085
+ headers: dataSource.headers,
1086
+ });
1088
1087
  // Handle OpenG2P response format (response_body.response_payload)
1089
1088
  if (response && typeof response === 'object') {
1090
1089
  if (response.response_body?.response_payload && Array.isArray(response.response_body.response_payload)) {
@@ -1107,8 +1106,8 @@ const getApiDataSource = async (dataSource, allValues, dataSourceRequestHandler,
1107
1106
  return [];
1108
1107
  }
1109
1108
  catch (error) {
1110
- console.error('Error fetching API data source:', error);
1111
- return [];
1109
+ // Rethrow so useBaseWidget's catch can log it with full widget context
1110
+ throw error;
1112
1111
  }
1113
1112
  };
1114
1113
  /**
@@ -2302,7 +2301,7 @@ const useBaseWidget = (options) => {
2302
2301
  dispatch(setDataSource({ widgetId, data: transformed }));
2303
2302
  }
2304
2303
  catch (error) {
2305
- console.error(`[useBaseWidget] ERROR loading data source for ${widgetId}:`, error);
2304
+ console.error(`[useBaseWidget] ERROR loading data source for widget "${widgetId}" (type="${dataSource.type}"):`, error, '\nWidget config:', config, '\ndataSourceRequestHandler provided:', Boolean(dataSourceRequestHandler));
2306
2305
  dispatch(setDataSource({ widgetId, data: [] }));
2307
2306
  }
2308
2307
  finally {
@@ -3807,7 +3806,15 @@ const collectWidgets = (panels) => {
3807
3806
  });
3808
3807
  return widgets;
3809
3808
  };
3810
- const sectionValidate = (section, currentSchemaData, dispatch) => {
3809
+ /**
3810
+ * Validate all widgets in a section and dispatch errors to the store.
3811
+ *
3812
+ * @param skipRequired - When true, required-field checks (widget-required,
3813
+ * validation.required, document-required) are skipped. Use this for
3814
+ * per-section Save/Next navigation so the user can advance without filling
3815
+ * every mandatory field; only format/range errors are reported.
3816
+ */
3817
+ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = false) => {
3811
3818
  const allWidgets = collectWidgets(section.panels);
3812
3819
  let isValid = true;
3813
3820
  for (const widget of allWidgets) {
@@ -3816,7 +3823,7 @@ const sectionValidate = (section, currentSchemaData, dispatch) => {
3816
3823
  continue;
3817
3824
  const widgetId = widget['widget-id'];
3818
3825
  const value = getWidgetValue(currentSchemaData, widget['widget-data-path'], widgetId);
3819
- const errors = validateWidget(value, widget['widget-data-validation'], widget['widget-required']);
3826
+ const errors = validateWidget(value, widget['widget-data-validation'], widget['widget-required'], skipRequired);
3820
3827
  if (errors.length > 0) {
3821
3828
  isValid = false;
3822
3829
  dispatch(setTouched({ widgetId, touched: true }));
@@ -3827,10 +3834,10 @@ const sectionValidate = (section, currentSchemaData, dispatch) => {
3827
3834
  dispatch(setError({ widgetId, errors: [] }));
3828
3835
  }
3829
3836
  }
3830
- // Supporting Documents
3837
+ // Supporting Documents — only check required when not skipping required validation
3831
3838
  section['section-supporting-documents']?.forEach((doc, index) => {
3832
3839
  const widgetId = `supporting-doc-${section['section-id']}-${index}`;
3833
- if (doc['document-required']) {
3840
+ if (!skipRequired && doc['document-required']) {
3834
3841
  const file = getValueByPath(currentSchemaData, doc['document-data-path']);
3835
3842
  if (!file) {
3836
3843
  isValid = false;
@@ -3936,7 +3943,7 @@ function scopedClassSelectors(sectionClassId, classNames) {
3936
3943
  * - Panels wrap when they exceed available width
3937
3944
  * - Sections can sit side-by-side if there's space
3938
3945
  */
3939
- const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton = false, mode = 'RegistryView', namespace, changeRequestType, showChangeRequestLabel = true, dbSectionId, sectionRegisterId, onSectionDirtyChange, sectionIndex, sectionCount, expandedSectionIndex, onExpandSection, onSectionSaveSuccess, onPreviousSection, isDraft, onEditModeChange, forceExitEdit, }) => {
3946
+ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton = false, mode = 'RegistryView', namespace, changeRequestType, showChangeRequestLabel = true, dbSectionId, sectionRegisterId, onSectionDirtyChange, sectionIndex, sectionCount, expandedSectionIndex, onExpandSection, onSectionSaveSuccess, onPreviousSection, isDraft, isAccessible = false, onEditModeChange, forceExitEdit, }) => {
3940
3947
  const { translateConfig, translate } = useWidgetTranslation();
3941
3948
  const resolvedTheme = useWidgetTheme();
3942
3949
  const portalCSSVariables = React.useMemo(() => themeToCSSVariables(resolvedTheme), [resolvedTheme]);
@@ -4002,16 +4009,25 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4002
4009
  const isExpandedFromContainer = typeof sectionIndex === 'number' && expandedSectionIndex === sectionIndex;
4003
4010
  const isExpandedStandalone = sectionIndex === undefined && standaloneExpanded;
4004
4011
  const isExpanded = mode === 'IntakeForm' && (isExpandedFromContainer || isExpandedStandalone);
4012
+ // IntakeForm only: tracks whether the user has clicked Next on this section at least once.
4013
+ // Used to unlock the accordion header so the user can navigate back to a visited section.
4014
+ const [hasBeenSavedByUser, setHasBeenSavedByUser] = React.useState(false);
4015
+ // Accordion header click behaviour in IntakeForm mode:
4016
+ // - Standalone (no sectionIndex): always toggleable.
4017
+ // - Managed by SectionsContainer: toggleable only when isAccessible is true
4018
+ // (i.e. the section has been visited OR is the immediate next one).
4019
+ // Sections beyond that remain locked.
4005
4020
  const handleAccordionToggle = React.useCallback(() => {
4006
4021
  if (mode !== 'IntakeForm')
4007
4022
  return;
4008
- if (typeof sectionIndex === 'number' && onExpandSection) {
4009
- onExpandSection(sectionIndex);
4010
- }
4011
- else if (sectionIndex === undefined) {
4023
+ if (sectionIndex === undefined) {
4012
4024
  setStandaloneExpanded(prev => !prev);
4013
4025
  }
4014
- }, [mode, sectionIndex, onExpandSection]);
4026
+ else if (isAccessible && onExpandSection) {
4027
+ onExpandSection(sectionIndex);
4028
+ }
4029
+ // Intentionally no-op for locked sections (isAccessible === false)
4030
+ }, [mode, sectionIndex, isAccessible, onExpandSection]);
4015
4031
  // Recursively count all vertical panels, especially those nested inside horizontal panels
4016
4032
  // Typically: horizontal panels at first level contain vertical panels at second level
4017
4033
  // Accounts for panel-column-span: a panel with column-span 3 counts as 3 columns
@@ -4346,8 +4362,6 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4346
4362
  const baselineSnapshotRef = React.useRef(null);
4347
4363
  // IntakeForm only: increment when baseline is updated after save - forces badge to update (refs don't trigger re-renders)
4348
4364
  const [intakeFormBaselineTrigger, setIntakeFormBaselineTrigger] = React.useState(0);
4349
- // IntakeForm only: tracks whether the user has actually saved this section (prevents "Saved" badge on initial load)
4350
- const [hasBeenSavedByUser, setHasBeenSavedByUser] = React.useState(false);
4351
4365
  // IntakeForm: treat as edit mode for dirty tracking when isDraft. RegistryView: use isEditMode.
4352
4366
  const effectiveEditModeForDirty = mode === 'IntakeForm' ? (isDraft !== false) : isEditMode;
4353
4367
  // Compute isDirty: compare current store state to baseline (only when in edit mode)
@@ -4465,7 +4479,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4465
4479
  const sectionWidgets = collectWidgets(originalSection.panels);
4466
4480
  const currentState = store.getState().widget;
4467
4481
  const currentSchemaData = currentState.values || {};
4468
- const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch);
4482
+ const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
4469
4483
  if (!isSectionValid) {
4470
4484
  return;
4471
4485
  }
@@ -4531,7 +4545,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4531
4545
  const sectionWidgets = collectWidgets(originalSection.panels);
4532
4546
  const currentState = store.getState().widget;
4533
4547
  const currentSchemaData = currentState.values || {};
4534
- const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch);
4548
+ const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
4535
4549
  if (!isSectionValid)
4536
4550
  return;
4537
4551
  const oldSchemaData = schemaData || contextSchemaData;
@@ -4580,6 +4594,11 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4580
4594
  }
4581
4595
  onSectionDirtyChange?.(sectionId, false);
4582
4596
  }
4597
+ else if (mode === 'IntakeForm') {
4598
+ // No onSectionSave provided, but still mark section as visited so the
4599
+ // user can navigate back to it by clicking the accordion header.
4600
+ setHasBeenSavedByUser(true);
4601
+ }
4583
4602
  // Always navigate to the next section
4584
4603
  onSectionSaveSuccess?.(sectionIndex);
4585
4604
  }, [store, onSectionSave, onSectionSaveSuccess, sectionIndex, originalSection, schemaData, contextSchemaData, namespace, hasSupportingDocuments, dbSectionId, sectionRegisterId, dispatch, buildSectionSnapshot, sectionId, onSectionDirtyChange, mode, isDraft]);
@@ -4798,13 +4817,17 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4798
4817
  .${sectionClassId}.intake-form-accordion-item .intake-form-accordion-header h2 {
4799
4818
  color: var(--owt-color-primary-dark, #F07B1A);
4800
4819
  }
4801
- .${sectionClassId}.intake-form-accordion-item .intake-form-accordion-header:hover {
4820
+ /* Hover / focus only shown when the header is actually interactive (standalone mode) */
4821
+ .${sectionClassId}.intake-form-accordion-item .intake-form-accordion-header[data-interactive="true"]:hover {
4802
4822
  opacity: 0.85;
4803
4823
  }
4804
- .${sectionClassId}.intake-form-accordion-item .intake-form-accordion-header:focus-visible {
4824
+ .${sectionClassId}.intake-form-accordion-item .intake-form-accordion-header[data-interactive="true"]:focus-visible {
4805
4825
  outline: 2px solid var(--owt-color-primary, #F5BB1A);
4806
4826
  outline-offset: 2px;
4807
4827
  }
4828
+ .${sectionClassId}.intake-form-accordion-item .intake-form-accordion-header[data-interactive="false"]:focus-visible {
4829
+ outline: none;
4830
+ }
4808
4831
  .${sectionClassId}.intake-form-accordion-item .intake-form-accordion-content {
4809
4832
  padding-top: 8px;
4810
4833
  padding-bottom: 0px;
@@ -4847,7 +4870,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4847
4870
  }),
4848
4871
  }, children: mode === 'IntakeForm' ? (
4849
4872
  /* IntakeForm: accordion layout - header always visible, content only when expanded */
4850
- jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsxs("button", { type: "button", id: `intake-form-accordion-header-${sectionId}`, className: "intake-form-accordion-header", onClick: handleAccordionToggle, "aria-expanded": isExpanded, "aria-controls": isExpanded ? `intake-form-accordion-content-${sectionId}` : undefined, style: {
4873
+ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsxs("button", { type: "button", id: `intake-form-accordion-header-${sectionId}`, className: "intake-form-accordion-header", onClick: handleAccordionToggle, "aria-expanded": isExpanded, "aria-controls": isExpanded ? `intake-form-accordion-content-${sectionId}` : undefined, "data-interactive": sectionIndex === undefined || isAccessible ? 'true' : 'false', style: {
4851
4874
  width: '100%',
4852
4875
  display: 'flex',
4853
4876
  alignItems: 'flex-start',
@@ -4857,7 +4880,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4857
4880
  marginBottom: 0,
4858
4881
  background: 'none',
4859
4882
  border: 'none',
4860
- cursor: 'pointer',
4883
+ cursor: sectionIndex === undefined || isAccessible ? 'pointer' : 'default',
4861
4884
  textAlign: 'left',
4862
4885
  fontFamily: 'Roboto, sans-serif',
4863
4886
  }, children: [jsxRuntimeExports.jsxs("div", { style: { flex: 1, display: 'flex', alignItems: 'center', gap: '12px', minWidth: 0 }, children: [jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold", style: { margin: 0 }, children: sectionToRender['section-title']
@@ -5155,6 +5178,11 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
5155
5178
  const dataSourceRequestHandler = propDataSourceRequestHandler || contextDataSourceRequestHandler;
5156
5179
  // IntakeForm mode: accordion state - which section is expanded (null = none; first expanded by default)
5157
5180
  const [expandedSectionIndex, setExpandedSectionIndex] = React.useState(0);
5181
+ // IntakeForm mode: high-water mark of the furthest section the user has clicked Next on.
5182
+ // A section at index i is accessible when i <= maxVisitedIndex + 1
5183
+ // (i.e. every visited section plus the one immediately after it).
5184
+ // Starts at -1 so only section 0 is accessible before any Next is clicked.
5185
+ const [maxVisitedIndex, setMaxVisitedIndex] = React.useState(-1);
5158
5186
  // RegistryView: track which section is currently in edit mode (by section-id); null = none
5159
5187
  const [editingSectionId, setEditingSectionId] = React.useState(null);
5160
5188
  const handleEditModeChange = React.useCallback((sectionId, editing) => {
@@ -5165,6 +5193,10 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
5165
5193
  // Stable ref for namespace so formHandle useMemo doesn't depend on the (possibly inline) function identity
5166
5194
  const namespaceRef = React.useRef(namespace);
5167
5195
  namespaceRef.current = namespace;
5196
+ // Stable refs so formHandle closure can access current mode and accordion setter without stale captures
5197
+ const modeRef = React.useRef(mode);
5198
+ modeRef.current = mode;
5199
+ const setExpandedSectionIndexRef = React.useRef(setExpandedSectionIndex);
5168
5200
  // Track dirty (unsaved changes) per section for form handle validation
5169
5201
  const sectionDirtyMapRef = React.useRef({});
5170
5202
  const handleSectionDirtyChange = React.useCallback((sectionId, isDirty) => {
@@ -5175,8 +5207,9 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
5175
5207
  const handleExpandSection = React.useCallback((index) => {
5176
5208
  setExpandedSectionIndex(prev => (prev === index ? null : index));
5177
5209
  }, []);
5178
- // IntakeForm mode: called after section save - collapse current, expand next
5210
+ // IntakeForm mode: called after section save - advance high-water mark, collapse current, expand next
5179
5211
  const handleSectionSaveSuccess = React.useCallback((index) => {
5212
+ setMaxVisitedIndex(prev => Math.max(prev, index));
5180
5213
  if (index + 1 < safeSections.length) {
5181
5214
  setExpandedSectionIndex(index + 1);
5182
5215
  }
@@ -5226,33 +5259,52 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
5226
5259
  };
5227
5260
  return {
5228
5261
  validate: async () => {
5229
- checkNoUnsavedChanges();
5262
+ if (modeRef.current !== 'IntakeForm')
5263
+ checkNoUnsavedChanges();
5230
5264
  const values = getValues();
5231
5265
  let allValid = true;
5266
+ let firstInvalidIndex = null;
5232
5267
  for (let i = 0; i < safeSections.length; i++) {
5233
5268
  const section = safeSections[i];
5234
5269
  const ns = getNamespace(section, i);
5235
5270
  const sectionToValidate = ns ? namespaceSectionConfig(section, ns) : section;
5236
5271
  const valid = sectionValidate(sectionToValidate, values, dispatch);
5237
- if (!valid)
5272
+ if (!valid) {
5273
+ if (firstInvalidIndex === null)
5274
+ firstInvalidIndex = i;
5238
5275
  allValid = false;
5276
+ }
5277
+ }
5278
+ if (!allValid && modeRef.current === 'IntakeForm' && firstInvalidIndex !== null) {
5279
+ setExpandedSectionIndexRef.current(firstInvalidIndex);
5239
5280
  }
5240
5281
  return allValid;
5241
5282
  },
5242
5283
  getFormData: () => getValues(),
5243
5284
  validateAndGetData: async () => {
5244
- checkNoUnsavedChanges();
5285
+ if (modeRef.current !== 'IntakeForm')
5286
+ checkNoUnsavedChanges();
5245
5287
  const values = getValues();
5246
5288
  const results = [];
5289
+ let firstInvalidIndex = null;
5247
5290
  for (let i = 0; i < safeSections.length; i++) {
5248
5291
  const section = safeSections[i];
5249
5292
  const ns = getNamespace(section, i);
5250
5293
  const sectionToValidate = ns ? namespaceSectionConfig(section, ns) : section;
5251
5294
  const valid = sectionValidate(sectionToValidate, values, dispatch);
5252
5295
  if (!valid) {
5253
- throw new Error('Validation failed');
5296
+ if (firstInvalidIndex === null)
5297
+ firstInvalidIndex = i;
5298
+ }
5299
+ else {
5300
+ results.push(buildSectionChanges(section, values, ns));
5254
5301
  }
5255
- results.push(buildSectionChanges(section, values, ns));
5302
+ }
5303
+ if (firstInvalidIndex !== null) {
5304
+ if (modeRef.current === 'IntakeForm') {
5305
+ setExpandedSectionIndexRef.current(firstInvalidIndex);
5306
+ }
5307
+ throw new Error('Validation failed. Please fix the errors and try again.');
5256
5308
  }
5257
5309
  return results;
5258
5310
  },
@@ -5351,6 +5403,8 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
5351
5403
  onSectionSaveSuccess: handleSectionSaveSuccess,
5352
5404
  onPreviousSection: handlePreviousSection,
5353
5405
  isDraft,
5406
+ // Accessible = every visited section + the one immediately after
5407
+ isAccessible: index <= maxVisitedIndex + 1,
5354
5408
  }
5355
5409
  : {};
5356
5410
  // RegistryView: single-edit coordination props
@@ -7333,7 +7387,7 @@ const NumberInputWidget = ({ config }) => {
7333
7387
  if (parsed === null) {
7334
7388
  // Allow empty input or partial input (e.g., "-", ".")
7335
7389
  if (inputValue === '' || inputValue === '-' || inputValue === '.') {
7336
- onChange('');
7390
+ onChange(null);
7337
7391
  }
7338
7392
  // Don't update if invalid - let user continue typing
7339
7393
  return;
@@ -9697,10 +9751,13 @@ const TextAreaWidget = ({ config }) => {
9697
9751
  // For readonly mode, render as preformatted text using <pre> tag
9698
9752
  if (isReadonly) {
9699
9753
  const displayValue = getStringValue() || '-';
9700
- return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] TextAreaDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("pre", { className: "text-base text-gray-900 font-medium whitespace-pre-wrap", title: String(displayValue), style: {
9754
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] TextAreaDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { title: String(displayValue), className: "text-base text-gray-900 font-medium overflow-y-auto whitespace-pre-wrap break-words", style: {
9701
9755
  fontFamily: 'Roboto, sans-serif',
9702
- margin: 0,
9703
- padding: 0,
9756
+ height: '56px',
9757
+ minHeight: '56px',
9758
+ maxHeight: '56px',
9759
+ lineHeight: '20px',
9760
+ padding: '8px 0',
9704
9761
  backgroundColor: 'transparent',
9705
9762
  border: 'none',
9706
9763
  }, children: displayValue }) })] }));