diff --git a/.storybook/main.js b/.storybook/main.js index fae84149..74e7d277 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,7 +1,7 @@ const path = require('path'); module.exports = { - stories: ['../stories/**/*.stories.js'], + stories: ['../stories/**/*.stories.js', '../stories/**/*.mdx'], addons: [ '@storybook/addon-essentials', '@storybook/addon-interactions', diff --git a/jest.config.js b/jest.config.js index eabaefbc..1e2449d3 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,6 +5,7 @@ module.exports = { '^src/(.*)$': '/src/$1', '^components/(.*)$': '/src/components/$1', '^helpers/(.*)$': '/src/helpers/$1', + '@carbon/styles/css/styles\\.css$': 'identity-obj-proxy', '\\.(css|less|scss|sass)$': 'identity-obj-proxy', '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'test-file-stub' }, diff --git a/package.json b/package.json index 8f25282b..9056df6c 100644 --- a/package.json +++ b/package.json @@ -27,27 +27,37 @@ }, "author": "bahmni@thoughtworks.com", "peerDependencies": { - "react": "^19.0.0", - "react-dom": "^19.0.0", "@bahmni/design-system": ">=0.0.1-dev.215", - "@carbon/react": ">=1.0.0" + "@carbon/react": ">=1.0.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { - "storybook": "^8.4.0", - "@storybook/react": "^8.4.0", - "@storybook/react-webpack5": "^8.4.0", - "@storybook/addon-actions": "^8.4.0", - "@storybook/addon-essentials": "^8.4.0", - "@storybook/addon-interactions": "^8.4.0", - "@storybook/blocks": "^8.4.0", - "@storybook/test": "^8.4.0", - "@bahmni/design-system": ">=0.0.1-dev.215", "@babel/cli": "^7.28.3", "@babel/core": "^7.28.4", "@babel/eslint-parser": "^7.28.4", "@babel/preset-env": "^7.28.3", "@babel/preset-react": "^7.27.1", + "@babel/runtime": "^7.28.0", + "@bahmni/design-system": ">=0.0.1-dev.215", + "@carbon/icons-react": "^11.0.0", + "@carbon/layout": "^11.0.0", + "@carbon/react": "^1.0.0", "@fortawesome/fontawesome-free": "^7.1.0", + "@storybook/addon-actions": "^8.4.0", + "@storybook/addon-backgrounds": "^8.4.0", + "@storybook/addon-controls": "^8.4.0", + "@storybook/addon-docs": "^8.4.0", + "@storybook/addon-essentials": "^8.4.0", + "@storybook/addon-interactions": "^8.4.0", + "@storybook/addon-measure": "^8.4.0", + "@storybook/addon-outline": "^8.4.0", + "@storybook/addon-toolbars": "^8.4.0", + "@storybook/addon-viewport": "^8.4.0", + "@storybook/blocks": "^8.4.0", + "@storybook/react": "^8.4.0", + "@storybook/react-webpack5": "^8.4.0", + "@storybook/test": "^8.4.0", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.1.0", @@ -65,9 +75,11 @@ "eslint-plugin-react": "^7.37.5", "fetch-mock": "^5.1.2", "file-loader": "^0.11.1", + "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "mini-css-extract-plugin": "^2.7.6", + "polished": "^4.3.1", "react": "^19.0.0", "react-dom": "^19.0.0", "react-json-syntax-highlighter": "^0.1.15", @@ -75,6 +87,7 @@ "rimraf": "^2.5.4", "sass": "^1.92.1", "sass-loader": "^13.3.2", + "storybook": "^8.4.0", "style-loader": "^3.3.3", "webpack": "^5.89.0", "webpack-cli": "^5.1.4", @@ -82,6 +95,7 @@ }, "dependencies": { "ajv": "^8.17.1", + "babel-runtime": "^6.26.0", "base64-inline-loader": "^1.1.0", "classnames": "^2.2.5", "immutable": "4.3.8", @@ -94,18 +108,6 @@ "sinon-as-promised": "^4.0.3", "whatwg-fetch": "^1.0.0" }, - "resolutions": { - "**/**/lodash": "^4.17.12", - "**/**/lodash-es": "^4.17.14", - "**/**/mixin-deep": "^1.3.2", - "**/**/set-value": "^2.0.1", - "**/**/decompress": "^4.2.1", - "**/**/xmlhttprequest-ssl": "^1.6.1", - "**/**/open": "^6.0.0", - "**/**/lodash.template": "^4.5.0", - "**/**/growl": "^1.10.0", - "**/**/handlebars": "^4.7.7" - }, "files": [ "dist/", "README.md", diff --git a/stories/AbnormalObsControl.stories.js b/stories/AbnormalObsControl.stories.js index 316b043a..36e75fbb 100644 --- a/stories/AbnormalObsControl.stories.js +++ b/stories/AbnormalObsControl.stories.js @@ -6,18 +6,19 @@ import { List } from 'immutable'; import StoryWrapper from './StoryWrapper'; import { registerCoreComponents } from './componentRegistry'; import { pulseDataMetadata } from './mockData'; +import { description, argTypes } from './_meta/abnormalObsControlMeta'; registerCoreComponents(); export default { title: 'Complex Controls/AbnormalObsControl', + component: ObsGroupControl, + tags: ['autodocs'], + argTypes, parameters: { docs: { description: { - component: - 'AbnormalObsControl demonstrates an ObsGroup that pairs a numeric observation with a ' + - 'Boolean "Abnormal" toggle button. The toggle is rendered inline using the Button display ' + - 'type and lets clinicians flag an out-of-range reading without leaving the field.', + component: description, }, }, }, diff --git a/stories/Forms.stories.js b/stories/Forms.stories.js index fa28e19a..60969b37 100644 --- a/stories/Forms.stories.js +++ b/stories/Forms.stories.js @@ -3,6 +3,7 @@ import StoryWrapper from './StoryWrapper'; import { Container } from 'src/components/Container.jsx'; import { runEventScript } from 'src/helpers/runEventScript'; import { SYSTOLIC_UUID, DIASTOLIC_UUID } from './mockData'; +import { description, argTypes } from './_meta/formsMeta'; const form = { id: 1, @@ -163,23 +164,13 @@ const obsList = [ export default { title: 'Example Forms/Lifecycle & Events', + component: Container, + tags: ['autodocs'], + argTypes, parameters: { docs: { description: { - component: ` -A guided tour of the form lifecycle. Each story documents one event or method exposed -by the form engine and shows it working on a real form. - -**Stories on this page:** - -- **Basic Data Binding** — foundation: how the Container renders pre-populated observations. -- **Form Lifecycle Demo** — all six lifecycle events on one interactive form with a live - Event Log: onFormInit, onValueChange, onValueUpdated, getValue(), onFormSave, Submit/Reset. -- **Event Flow Diagram** — the complete init → change → submit → post-save timeline. -- **Handler Templates** — copy-pasteable handler snippets for the most common patterns. -- **Accessibility for Events** — keyboard and assistive-technology considerations when - events update the form dynamically. - `, + component: description, }, }, }, diff --git a/stories/MultiSelect.stories.js b/stories/MultiSelect.stories.js index 47d345dc..f45f50c3 100644 --- a/stories/MultiSelect.stories.js +++ b/stories/MultiSelect.stories.js @@ -1,6 +1,7 @@ import React from 'react'; import StoryWrapper from './StoryWrapper'; import { Container } from 'src/components/Container.jsx'; +import { description, argTypes } from './_meta/multiSelectMeta'; const form = { id: 1, @@ -321,13 +322,13 @@ const obsList = [ export default { title: 'Atomic Controls/MultiSelect', + component: Container, + tags: ['autodocs'], + argTypes, parameters: { docs: { description: { - component: - 'Demonstrates a form containing a multi-select coded observation (Tuberculosis Comorbidity). ' + - 'Multi-select obs allow clinicians to choose multiple answers for a single concept and store ' + - 'each answer as a separate observation record.', + component: description, }, }, }, diff --git a/stories/ObsControl.stories.js b/stories/ObsControl.stories.js index 1666cd32..26d3cbea 100644 --- a/stories/ObsControl.stories.js +++ b/stories/ObsControl.stories.js @@ -3,6 +3,7 @@ import { ObsControlWithIntl as ObsControl } from 'src/components/ObsControl.jsx' import StoryWrapper from './StoryWrapper'; import '../styles/styles.scss'; import { SYSTOLIC_UUID, DIASTOLIC_UUID } from './mockData'; +import { description, argTypes } from './_meta/obsControlMeta'; const form = { controls: [ @@ -106,35 +107,12 @@ const emptyValue = { value: undefined, comment: undefined, interpretation: undef export default { title: 'Complex Controls/ObsControl', component: ObsControl, - argTypes: { - validate: { control: 'boolean', description: 'Trigger field-level validation' }, - validateForm: { control: 'boolean', description: 'Trigger form-level validation' }, - onValueChanged: { action: 'onValueChanged' }, - showNotification: { action: 'showNotification' }, - onControlAdd: { action: 'onControlAdd' }, - onControlRemove: { action: 'onControlRemove' }, - }, + tags: ['autodocs'], + argTypes, parameters: { docs: { description: { - component: - 'ObsControl is the primary observation control that binds a single concept to a form field. ' + - 'It wraps the underlying input widget (NumericBox, TextBox, BooleanControl, CodedControl, etc.) ' + - 'based on the concept datatype resolved at render time via componentStore.\n\n' + - '**Concept binding**: The `metadata.concept` object identifies which OpenMRS concept is being ' + - 'captured. The `concept.datatype` drives which widget renders (Numeric → NumericBox, ' + - 'Text → TextBox, Boolean → BooleanControl / Button, Coded → AutoComplete or DropDown).\n\n' + - '**Value wrapping**: ObsControl receives and emits values as `{ value, comment, interpretation }` ' + - 'objects. When a user changes the input the `onValueChanged(formFieldPath, value, errors)` ' + - 'callback is fired with the updated wrapped value so the parent Container can update its ' + - 'ControlRecordTree.\n\n' + - '**Add More**: When `properties.addMore` is true, the control renders Add / Remove buttons ' + - 'via AddMoreDecorator, allowing the clinician to capture repeated observations for the same ' + - 'concept within a single encounter.\n\n' + - 'Accessibility (WCAG 2.1 AA): Each input is associated with a `