diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95a86a6..fc0834b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,9 +10,14 @@ on: pull_request: branches: [main] +permissions: + id-token: write # Required for OIDC + contents: read + jobs: build: runs-on: ubuntu-latest + timeout-minutes: 20 steps: - name: Git checkout uses: actions/checkout@v4 @@ -22,7 +27,8 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "20" + node-version: "24" + registry-url: "https://registry.npmjs.org" - name: Cache node_modules uses: actions/cache@v4 @@ -32,9 +38,9 @@ jobs: ~/.nvm ~/work/farjs-ui/farjs-ui/node_modules ~/work/farjs-ui/farjs-ui/package-lock.json - key: ${{ runner.os }}-node_modules-cache-v3-${{ hashFiles('package.json') }} + key: ${{ runner.os }}-node_modules-cache-v1-${{ hashFiles('package.json') }} restore-keys: | - ${{ runner.os }}-node_modules-cache-v3- + ${{ runner.os }}-node_modules-cache-v1- - name: Extract Tag Name run: echo "TAG_NAME=$(echo ${GITHUB_REF##*/})" >> $GITHUB_ENV @@ -64,11 +70,8 @@ jobs: - name: Publish RELEASE run: | - echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" > .npmrc VERSION="$(echo "$TAG_NAME" | cut -d'v' -f 2)" echo "Publish a release version=$VERSION for tag $TAG_NAME" npm --no-git-tag-version --allow-same-version version $VERSION npm publish if: ${{ env.TAG_NAME != '' }} - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/ci_bun.yml b/.github/workflows/ci_bun.yml index f9fad2f..4f76baa 100644 --- a/.github/workflows/ci_bun.yml +++ b/.github/workflows/ci_bun.yml @@ -20,7 +20,7 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v1 with: - bun-version: "1.0.1" + bun-version: "1.2.0" - name: Cache Dependencies uses: actions/cache@v4 @@ -28,9 +28,9 @@ jobs: path: | ~/.bun/install/cache ~/work/farjs-ui/farjs-ui/bun.lockb - key: ${{ runner.os }}-bun-cache-v4-${{ hashFiles('package.json') }} + key: ${{ runner.os }}-bun-cache-v5-${{ hashFiles('package.json') }} restore-keys: | - ${{ runner.os }}-bun-cache-v4- + ${{ runner.os }}-bun-cache-v5- - name: Run tests run: | diff --git a/package.json b/package.json index 879c7ba..6236a1b 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "bun": ">=0.8" }, "dependencies": { - "@farjs/blessed": "0.3.2", + "@farjs/blessed": "0.4.0", "react": "^17.0.1", "react-blessed": "0.7.2" }, @@ -54,8 +54,8 @@ "mock-fn": "^1.1.0", "prettier": "^2.8.8", "quick-lint-js": "^3.0.0", - "react-assert": "^1.1.0", + "react-assert": "^1.2.0", "react-test-renderer": "^17.0.1", - "typescript": "^4.9.5" + "typescript": "^5.7.3" } } diff --git a/src/ButtonsPanel.mjs b/src/ButtonsPanel.mjs index a349f38..da6bd5e 100644 --- a/src/ButtonsPanel.mjs +++ b/src/ButtonsPanel.mjs @@ -13,7 +13,7 @@ const h = React.createElement; /** * @typedef {{ * readonly top: number; - * readonly actions: ButtonsPanelAction[]; + * readonly actions: readonly ButtonsPanelAction[]; * readonly style: import("@farjs/blessed").Widgets.Types.TStyle; * readonly padding?: number; * readonly margin?: number; diff --git a/src/ComboBox.mjs b/src/ComboBox.mjs index f6ab975..6e14f8b 100644 --- a/src/ComboBox.mjs +++ b/src/ComboBox.mjs @@ -18,7 +18,7 @@ const h = React.createElement; * readonly left: number; * readonly top: number; * readonly width: number; - * readonly items: string[]; + * readonly items: readonly string[]; * readonly value: string; * onChange(value: string): void; * onEnter?(): void; @@ -34,8 +34,6 @@ const ComboBox = (props) => { const inputRef = /** @type {React.MutableRefObject} */ ( useRef() ); - const programRef = - /** @type {React.MutableRefObject} */ (useRef(null)); const autoCompleteTimeoutRef = /** @type {React.MutableRefObject} */ (useRef(null)); @@ -62,16 +60,18 @@ const ComboBox = (props) => { function showPopup(viewport) { setPopup(viewport); - if (programRef.current) { - programRef.current.hideCursor(); + const el = inputRef.current; + if (el) { + el.screen.program.hideCursor(); } } function hidePopup() { setPopup(null); - if (programRef.current) { - programRef.current.showCursor(); + const el = inputRef.current; + if (el) { + el.screen.program.showCursor(); } } @@ -199,12 +199,6 @@ const ComboBox = (props) => { ? h( "form", { - /** @type {(el?: BlessedElement) => void} */ - ref: (el) => { - if (el) { - programRef.current = el.screen.program; - } - }, clickable: true, mouse: true, autoFocus: false, diff --git a/src/ComboBoxPopup.mjs b/src/ComboBoxPopup.mjs index 97fab8e..c91cd41 100644 --- a/src/ComboBoxPopup.mjs +++ b/src/ComboBoxPopup.mjs @@ -13,7 +13,7 @@ const h = React.createElement; * readonly left: number; * readonly top: number; * readonly width: number; - * readonly items: string[]; + * readonly items: readonly string[]; * readonly style: import("@farjs/blessed").Widgets.Types.TStyle; * readonly viewport: ListViewport; * setViewport(viewport: ListViewport): void; diff --git a/src/ListBox.mjs b/src/ListBox.mjs index 32cd8a2..1cc0f29 100644 --- a/src/ListBox.mjs +++ b/src/ListBox.mjs @@ -15,7 +15,7 @@ const h = React.createElement; * readonly width: number; * readonly height: number; * readonly style: import("@farjs/blessed").Widgets.Types.TStyle; - * readonly items: string[]; + * readonly items: readonly string[]; * readonly selected: number; * onAction(index: number): void; * onSelect?(index: number): void; diff --git a/src/ListView.mjs b/src/ListView.mjs index f014c34..e71e008 100644 --- a/src/ListView.mjs +++ b/src/ListView.mjs @@ -16,7 +16,7 @@ const h = React.createElement; * readonly top: number; * readonly width: number; * readonly height: number; - * readonly items: string[]; + * readonly items: readonly string[]; * readonly style: BlessedStyle; * readonly viewport: ListViewport; * setViewport(viewport: ListViewport): void; @@ -26,10 +26,10 @@ const h = React.createElement; /** * @param {number} selected - * @param {string[]} items + * @param {readonly string[]} items * @param {number} width * @param {BlessedStyle} theme - * @returns {string[]} + * @returns {readonly string[]} */ function renderItems(selected, items, width, theme) { return items.map((item, index) => { diff --git a/src/TextInput.mjs b/src/TextInput.mjs index cf3bd60..2e306fc 100644 --- a/src/TextInput.mjs +++ b/src/TextInput.mjs @@ -82,7 +82,7 @@ const TextInput = (props) => { /** * @param {number} [dx] - * @returns {number[]} + * @returns {readonly number[]} */ function moveLeft(dx) { const ldx = dx ?? Math.max(charStart.lcw, 1); @@ -91,7 +91,7 @@ const TextInput = (props) => { /** * @param {number} [dx] - * @returns {number[]} + * @returns {readonly number[]} */ //prettier-ignore function moveRight(dx) { diff --git a/src/UI.mjs b/src/UI.mjs index b828b32..45d4e78 100644 --- a/src/UI.mjs +++ b/src/UI.mjs @@ -33,7 +33,7 @@ export function renderText(isBold, fgColor, bgColor, text) { /** * @param {string} text * @param {number} maxLen - * @returns {string[]} + * @returns {readonly string[]} */ export function splitText(text, maxLen) { const sentences = text.split("\n"); diff --git a/src/menu/BottomMenu.mjs b/src/menu/BottomMenu.mjs index 2ed6743..5801612 100644 --- a/src/menu/BottomMenu.mjs +++ b/src/menu/BottomMenu.mjs @@ -6,7 +6,7 @@ const h = React.createElement; /** * @typedef {{ - * readonly items: string[]; + * readonly items: readonly string[]; * }} BottomMenuProps */ diff --git a/src/menu/BottomMenuView.mjs b/src/menu/BottomMenuView.mjs index e6066dc..9a85ee5 100644 --- a/src/menu/BottomMenuView.mjs +++ b/src/menu/BottomMenuView.mjs @@ -6,7 +6,7 @@ const h = React.createElement; /** * @typedef {{ * readonly width: number; - * readonly items: string[]; + * readonly items: readonly string[]; * }} BottomMenuViewProps */ @@ -29,7 +29,7 @@ const BottomMenuView = (props) => { const itemsCount = items.length; const itemWidth = Math.trunc(width / itemsCount); - /** @type {BottomMenuViewItem[]} */ + /** @type {readonly BottomMenuViewItem[]} */ const itemsWithPos = items.map((item, index) => { const leftPos = index * itemWidth; diff --git a/src/menu/MenuBar.mjs b/src/menu/MenuBar.mjs index 755dba6..0c6e923 100644 --- a/src/menu/MenuBar.mjs +++ b/src/menu/MenuBar.mjs @@ -12,13 +12,13 @@ const h = React.createElement; /** * @typedef {{ * readonly label: string; - * readonly subItems: string[]; + * readonly subItems: readonly string[]; * }} MenuBarItem */ /** * @typedef {{ - * readonly items: MenuBarItem[]; + * readonly items: readonly MenuBarItem[]; * onAction(menuIndex: number, subIndex: number): void; * onClose(): void; * }} MenuBarProps @@ -45,7 +45,7 @@ const MenuBar = (props) => { const width = props.items.reduce((res, item) => { return res + item.label.length + padding * 2; }, 0); - /** @type {ButtonsPanelAction[]} */ + /** @type {readonly ButtonsPanelAction[]} */ const actions = props.items.map((item, index) => { return { label: item.label, diff --git a/src/menu/MenuPopup.mjs b/src/menu/MenuPopup.mjs index 5e0fbd3..23c80b2 100644 --- a/src/menu/MenuPopup.mjs +++ b/src/menu/MenuPopup.mjs @@ -12,7 +12,7 @@ const paddingVertical = 1; /** * @typedef {{ * readonly title: string; - * readonly items: string[]; + * readonly items: readonly string[]; * getLeft(width: number): string; * onSelect(index: number): void; * onClose(): void; diff --git a/src/menu/SubMenu.mjs b/src/menu/SubMenu.mjs index 2c143ec..5fbfcc6 100644 --- a/src/menu/SubMenu.mjs +++ b/src/menu/SubMenu.mjs @@ -10,7 +10,7 @@ const h = React.createElement; /** * @typedef {{ * readonly selected: number; - * readonly items: string[]; + * readonly items: readonly string[]; * readonly top: number; * readonly left: number; * onClick(index: number): void; diff --git a/src/popup/ListPopup.mjs b/src/popup/ListPopup.mjs index fbc9293..bcb8a2c 100644 --- a/src/popup/ListPopup.mjs +++ b/src/popup/ListPopup.mjs @@ -14,7 +14,7 @@ const h = React.createElement; /** * @typedef {{ * readonly title: string; - * readonly items: string[]; + * readonly items: readonly string[]; * onAction(index: number): void; * onClose(): void; * readonly selected?: number; diff --git a/src/popup/MessageBox.mjs b/src/popup/MessageBox.mjs index 7db03c7..d3c56fe 100644 --- a/src/popup/MessageBox.mjs +++ b/src/popup/MessageBox.mjs @@ -16,7 +16,7 @@ const h = React.createElement; * @typedef {{ * readonly title: string; * readonly message: string; - * readonly actions: MessageBoxAction[]; + * readonly actions: readonly MessageBoxAction[]; * readonly style: import("@farjs/blessed").Widgets.Types.TStyle; * }} MessageBoxProps */ @@ -34,7 +34,7 @@ const MessageBox = (props) => { const height = (ModalContent.paddingVertical + 1) * 2 + textLines.length + 1; const onClose = props.actions.find((a) => a.triggeredOnClose)?.onAction; - /** @type {ButtonsPanelAction[]} */ + /** @type {readonly ButtonsPanelAction[]} */ const actions = props.actions.map((action) => { return { label: action.label, diff --git a/src/popup/MessageBoxAction.mjs b/src/popup/MessageBoxAction.mjs index 48b34df..3576dc3 100644 --- a/src/popup/MessageBoxAction.mjs +++ b/src/popup/MessageBoxAction.mjs @@ -8,10 +8,10 @@ /** * @param {string} label - * @param {boolean} triggeredOnClose + * @param {boolean} [triggeredOnClose] * @returns {(onAction: () => void) => MessageBoxAction} */ -function createAction(label, triggeredOnClose = false) { +function MessageBoxAction(label, triggeredOnClose = false) { return (onAction) => { return { label, @@ -21,10 +21,8 @@ function createAction(label, triggeredOnClose = false) { }; } -const MessageBoxAction = { - OK: createAction("OK", true), - YES: createAction("YES"), - NO: createAction("NO", true), -}; +MessageBoxAction.OK = MessageBoxAction("OK", true); +MessageBoxAction.YES = MessageBoxAction("YES"); +MessageBoxAction.NO = MessageBoxAction("NO", true); export default MessageBoxAction; diff --git a/src/portal/WithPortals.mjs b/src/portal/WithPortals.mjs index b1f95a5..08ecc97 100644 --- a/src/portal/WithPortals.mjs +++ b/src/portal/WithPortals.mjs @@ -35,7 +35,7 @@ const WithPortals = { */ const WithPortalsComp = (props) => { const [portals, setPortals] = useState( - /** @type {Array} */ ([]) + /** @type {readonly PortalItem[]} */ ([]) ); /** @type {WithPortalsContext} */ diff --git a/src/task/TaskManagerUi.mjs b/src/task/TaskManagerUi.mjs index d89841e..cdb3894 100644 --- a/src/task/TaskManagerUi.mjs +++ b/src/task/TaskManagerUi.mjs @@ -36,7 +36,9 @@ function stripErrorPrefix(error) { const TaskManagerUi = (props) => { const { statusPopupComp, messageBoxComp } = TaskManagerUi; - const [errors, updateErrors] = useState(/** @type {string[]} */ ([])); + const [errors, updateErrors] = useState( + /** @type {readonly string[]} */ ([]) + ); const statusMessage = (props.showLoading ? props.status : undefined) ?? ""; const errorMessage = (props.error ?? "").trim(); const theme = Theme.useTheme().popup; diff --git a/test/Button.test.mjs b/test/Button.test.mjs index 43e5c31..4790786 100644 --- a/test/Button.test.mjs +++ b/test/Button.test.mjs @@ -43,11 +43,15 @@ describe("Button.test.mjs", () => { assertButton(renderer.root, props, false); //when & then - button.props.onFocus(); + TestRenderer.act(() => { + button.props.onFocus(); + }); assertButton(renderer.root, props, true); //when & then - button.props.onBlur(); + TestRenderer.act(() => { + button.props.onBlur(); + }); assertButton(renderer.root, props, false); }); diff --git a/test/ButtonsPanel.test.mjs b/test/ButtonsPanel.test.mjs index 40403c5..529e0f0 100644 --- a/test/ButtonsPanel.test.mjs +++ b/test/ButtonsPanel.test.mjs @@ -120,7 +120,7 @@ function getAction(label, onAction) { } /** - * @param {ButtonsPanelAction[]} actions + * @param {readonly ButtonsPanelAction[]} actions * @param {number} [padding] * @param {number} [margin] * @returns {ButtonsPanelProps} @@ -145,7 +145,7 @@ function getButtonsPanelProps(actions, padding, margin) { /** * @param {TestRenderer.ReactTestInstance} result * @param {ButtonsPanelProps} props - * @param {{action: string, pos: number}[]} actions + * @param {readonly {action: string, pos: number}[]} actions */ function assertButtonsPanel(result, props, actions) { const buttonsWidth = diff --git a/test/ComboBox.test.mjs b/test/ComboBox.test.mjs index 9638763..ed450ab 100644 --- a/test/ComboBox.test.mjs +++ b/test/ComboBox.test.mjs @@ -54,14 +54,18 @@ describe("ComboBox.test.mjs", () => { process.stdin.on("keypress", keyListener); const props = { ...getComboBoxProps(), onChange }; const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props))); - assert.deepEqual( - renderer.root.findByType(textInputComp).props.onKeypress("C-down"), - true - ); + TestRenderer.act(() => { + assert.deepEqual( + renderer.root.findByType(textInputComp).props.onKeypress("C-down"), + true + ); + }); const comboBox = renderer.root.findByType(comboBoxPopup).props; //when - comboBox.onClick(1); + TestRenderer.act(() => { + comboBox.onClick(1); + }); //cleanup process.stdin.removeListener("keypress", keyListener); @@ -91,15 +95,19 @@ describe("ComboBox.test.mjs", () => { process.stdin.on("keypress", keyListener); const props = { ...getComboBoxProps(), onChange }; const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props))); - assert.deepEqual( - renderer.root.findByType(textInputComp).props.onKeypress("C-down"), - true - ); + TestRenderer.act(() => { + assert.deepEqual( + renderer.root.findByType(textInputComp).props.onKeypress("C-down"), + true + ); + }); assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 1); const textInput = renderer.root.findByType(textInputComp).props; //when - assert.deepEqual(textInput.onKeypress("return"), true); + TestRenderer.act(() => { + assert.deepEqual(textInput.onKeypress("return"), true); + }); //cleanup process.stdin.removeListener("keypress", keyListener); @@ -117,15 +125,19 @@ describe("ComboBox.test.mjs", () => { process.stdin.on("keypress", keyListener); const props = { ...getComboBoxProps(), items: [], onChange }; const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props))); - assert.deepEqual( - renderer.root.findByType(textInputComp).props.onKeypress("C-down"), - true - ); + TestRenderer.act(() => { + assert.deepEqual( + renderer.root.findByType(textInputComp).props.onKeypress("C-down"), + true + ); + }); assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 1); const textInput = renderer.root.findByType(textInputComp).props; //when - assert.deepEqual(textInput.onKeypress("return"), true); + TestRenderer.act(() => { + assert.deepEqual(textInput.onKeypress("return"), true); + }); //cleanup process.stdin.removeListener("keypress", keyListener); @@ -163,15 +175,19 @@ describe("ComboBox.test.mjs", () => { const textInput = /** @type {TextInputProps} */ ( renderer.root.findByType(textInputComp).props ); - textInput.stateUpdater((s) => { - return { ...s, selStart: 1 }; + TestRenderer.act(() => { + textInput.stateUpdater((s) => { + return { ...s, selStart: 1 }; + }); }); //when - assert.deepEqual( - renderer.root.findByType(textInputComp).props.onKeypress("b"), - false - ); + TestRenderer.act(() => { + assert.deepEqual( + renderer.root.findByType(textInputComp).props.onKeypress("b"), + false + ); + }); //then await setTimeout(autoCompleteTimeoutMs); @@ -357,15 +373,19 @@ describe("ComboBox.test.mjs", () => { //given const props = getComboBoxProps(); const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props))); - assert.deepEqual( - renderer.root.findByType(textInputComp).props.onKeypress("C-down"), - true - ); + TestRenderer.act(() => { + assert.deepEqual( + renderer.root.findByType(textInputComp).props.onKeypress("C-down"), + true + ); + }); assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 1); const textInput = renderer.root.findByType(textInputComp).props; //when - assert.deepEqual(textInput.onKeypress("escape"), true); + TestRenderer.act(() => { + assert.deepEqual(textInput.onKeypress("escape"), true); + }); //then assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 0); @@ -375,15 +395,19 @@ describe("ComboBox.test.mjs", () => { //given const props = getComboBoxProps(); const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props))); - assert.deepEqual( - renderer.root.findByType(textInputComp).props.onKeypress("C-up"), - true - ); + TestRenderer.act(() => { + assert.deepEqual( + renderer.root.findByType(textInputComp).props.onKeypress("C-up"), + true + ); + }); assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 1); const textInput = renderer.root.findByType(textInputComp).props; //when - assert.deepEqual(textInput.onKeypress("C-up"), true); + TestRenderer.act(() => { + assert.deepEqual(textInput.onKeypress("C-up"), true); + }); //then assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 0); @@ -396,22 +420,24 @@ describe("ComboBox.test.mjs", () => { const showCursor = mockFunction(); const program = { hideCursor, showCursor }; const screen = { program }; - const formMock = { screen }; - const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props)), { - createNodeMock: (el) => { - return el.type === "form" ? formMock : null; - }, + const textMock = { screen }; + const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props))); + const textInput = renderer.root.findByType(textInputComp).props; + textInput.inputRef.current = textMock; + TestRenderer.act(() => { + assert.deepEqual( + renderer.root.findByType(textInputComp).props.onKeypress("C-up"), + true + ); }); - assert.deepEqual( - renderer.root.findByType(textInputComp).props.onKeypress("C-up"), - true - ); assert.deepEqual(hideCursor.times, 1); assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 1); const form = renderer.root.findByType("form"); //when - form.props.onClick(); + TestRenderer.act(() => { + form.props.onClick(); + }); //then assert.deepEqual(showCursor.times, 1); @@ -421,29 +447,33 @@ describe("ComboBox.test.mjs", () => { it("should hide popup and show cursor when arrow.onClick", () => { //given const props = getComboBoxProps(); + const focus = mockFunction(); const hideCursor = mockFunction(); const showCursor = mockFunction(); const program = { hideCursor, showCursor }; const screen = { program }; - const formMock = { screen }; - const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props)), { - createNodeMock: (el) => { - return el.type === "form" ? formMock : null; - }, + const textMock = { screen, focus }; + const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props))); + const textInput = renderer.root.findByType(textInputComp).props; + textInput.inputRef.current = textMock; + TestRenderer.act(() => { + assert.deepEqual( + renderer.root.findByType(textInputComp).props.onKeypress("C-up"), + true + ); }); - assert.deepEqual( - renderer.root.findByType(textInputComp).props.onKeypress("C-up"), - true - ); assert.deepEqual(hideCursor.times, 1); assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 1); const arrow = renderer.root.findByType("text"); //when - arrow.props.onClick(); + TestRenderer.act(() => { + arrow.props.onClick(); + }); //then assert.deepEqual(showCursor.times, 1); + assert.deepEqual(focus.times, 1); assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 0); }); @@ -451,7 +481,10 @@ describe("ComboBox.test.mjs", () => { //given const props = getComboBoxProps(); const focus = mockFunction(); - const screen = {}; + const hideCursor = mockFunction(); + const showCursor = mockFunction(); + const program = { hideCursor, showCursor }; + const screen = { program, focused: {} }; const textMock = { screen, focus }; screen.focused = textMock; const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props))); @@ -461,10 +494,14 @@ describe("ComboBox.test.mjs", () => { const arrow = renderer.root.findByType("text"); //when - arrow.props.onClick(); + TestRenderer.act(() => { + arrow.props.onClick(); + }); //then assert.deepEqual(focus.times, 0); + assert.deepEqual(hideCursor.times, 1); + assert.deepEqual(showCursor.times, 0); assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 1); }); @@ -472,7 +509,10 @@ describe("ComboBox.test.mjs", () => { //given const props = getComboBoxProps(); const focus = mockFunction(); - const screen = { focused: {} }; + const hideCursor = mockFunction(); + const showCursor = mockFunction(); + const program = { hideCursor, showCursor }; + const screen = { program, focused: {} }; const textMock = { screen, focus }; const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props))); const textInput = renderer.root.findByType(textInputComp).props; @@ -481,10 +521,14 @@ describe("ComboBox.test.mjs", () => { const arrow = renderer.root.findByType("text"); //when - arrow.props.onClick(); + TestRenderer.act(() => { + arrow.props.onClick(); + }); //then assert.deepEqual(focus.times, 1); + assert.deepEqual(hideCursor.times, 1); + assert.deepEqual(showCursor.times, 0); assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 1); }); @@ -493,17 +537,21 @@ describe("ComboBox.test.mjs", () => { const items = ["1", "2", "3"]; const props = { ...getComboBoxProps(), items }; const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props))); - assert.deepEqual( - renderer.root.findByType(textInputComp).props.onKeypress("C-down"), - true - ); + TestRenderer.act(() => { + assert.deepEqual( + renderer.root.findByType(textInputComp).props.onKeypress("C-down"), + true + ); + }); const popup = renderer.root.findByType(comboBoxPopup).props; assert.deepEqual(popup.items, items); assert.deepEqual(popup.viewport.focused, 0); const viewport = popup.viewport.updated(popup.viewport.offset, 1); //when - popup.setViewport(viewport); + TestRenderer.act(() => { + popup.setViewport(viewport); + }); //then assert.deepEqual( @@ -531,17 +579,21 @@ describe("ComboBox.test.mjs", () => { //given const props = getComboBoxProps(); const renderer = TestRenderer.create(withThemeContext(h(ComboBox, props))); - assert.deepEqual( - renderer.root.findByType(textInputComp).props.onKeypress("C-up"), - true - ); + TestRenderer.act(() => { + assert.deepEqual( + renderer.root.findByType(textInputComp).props.onKeypress("C-up"), + true + ); + }); assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 1); const textInput = renderer.root.findByType(textInputComp).props; //when & then - assert.deepEqual(textInput.onKeypress("up"), true); - assert.deepEqual(textInput.onKeypress("down"), true); - assert.deepEqual(textInput.onKeypress("unknown"), true); + TestRenderer.act(() => { + assert.deepEqual(textInput.onKeypress("up"), true); + assert.deepEqual(textInput.onKeypress("down"), true); + assert.deepEqual(textInput.onKeypress("unknown"), true); + }); //then assert.deepEqual(renderer.root.findAllByType(comboBoxPopup).length, 1); @@ -556,8 +608,10 @@ describe("ComboBox.test.mjs", () => { ); //when & then - textInput.stateUpdater((s) => { - return { ...s, offset: 1, cursorX: 2, selStart: 3, selEnd: 4 }; + TestRenderer.act(() => { + textInput.stateUpdater((s) => { + return { ...s, offset: 1, cursorX: 2, selStart: 3, selEnd: 4 }; + }); }); //then @@ -578,7 +632,9 @@ describe("ComboBox.test.mjs", () => { const textInput = renderer.root.findByType(textInputComp).props; //when - assert.deepEqual(textInput.onKeypress("C-down"), true); + TestRenderer.act(() => { + assert.deepEqual(textInput.onKeypress("C-down"), true); + }); //then assertComboBox(renderer.root, props, true); diff --git a/test/ComboBoxPopup.test.mjs b/test/ComboBoxPopup.test.mjs index a269821..903fe52 100644 --- a/test/ComboBoxPopup.test.mjs +++ b/test/ComboBoxPopup.test.mjs @@ -141,7 +141,7 @@ describe("ComboBoxPopup.test.mjs", () => { /** * @typedef {{ * index: number, - * items: string[], + * items: readonly string[], * setViewport(viewport: ListViewport): void, * onClick(index: number): void * }} DefaultProps diff --git a/test/ListBox.test.mjs b/test/ListBox.test.mjs index 1ff82e7..ada45bc 100644 --- a/test/ListBox.test.mjs +++ b/test/ListBox.test.mjs @@ -37,7 +37,9 @@ describe("ListBox.test.mjs", () => { const offset = 1; //when - scrollBar.props.onChange(offset); + TestRenderer.act(() => { + scrollBar.props.onChange(offset); + }); //then assertListBox(renderer.root, props, true, offset); @@ -61,7 +63,9 @@ describe("ListBox.test.mjs", () => { const viewport = listView.viewport.updated(listView.viewport.offset, 0); //when - listView.setViewport(viewport); + TestRenderer.act(() => { + listView.setViewport(viewport); + }); //then assert.deepEqual(onSelect.times, 2); @@ -85,7 +89,9 @@ describe("ListBox.test.mjs", () => { ); //when - listView.setViewport(viewport); + TestRenderer.act(() => { + listView.setViewport(viewport); + }); //then assert.deepEqual(onSelect.times, 1); @@ -107,7 +113,9 @@ describe("ListBox.test.mjs", () => { const button = renderer.root.findByType("button"); //when - button.props.onKeypress(null, { full: "down" }); + TestRenderer.act(() => { + button.props.onKeypress(null, { full: "down" }); + }); //then assert.deepEqual(onSelect.times, 2); @@ -168,7 +176,7 @@ describe("ListBox.test.mjs", () => { * width: number, * height: number, * selected: number, - * items: string[], + * items: readonly string[], * onAction(index: number): void, * onSelect?(index: number): void * }} DefaultProps diff --git a/test/ListView.test.mjs b/test/ListView.test.mjs index 7dc85ba..dca652c 100644 --- a/test/ListView.test.mjs +++ b/test/ListView.test.mjs @@ -201,7 +201,7 @@ describe("ListView.test.mjs", () => { * width: number, * height: number, * index: number, - * items: string[], + * items: readonly string[], * setViewport(viewport: ListViewport): void, * onClick(index: number): void * }} DefaultProps @@ -251,7 +251,7 @@ function assertListViewport(result, offset, focused, length, viewLength) { /** * @param {TestRenderer.ReactTestInstance} result * @param {ListViewProps} props - * @param {string[]} expectedContent + * @param {readonly string[]} expectedContent */ function assertListView(result, props, expectedContent) { assert.deepEqual(ListView.displayName, "ListView"); diff --git a/test/TextBox.test.mjs b/test/TextBox.test.mjs index d442e27..6ec64dd 100644 --- a/test/TextBox.test.mjs +++ b/test/TextBox.test.mjs @@ -39,8 +39,10 @@ describe("TextBox.test.mjs", () => { }); //when - textInput.stateUpdater((s) => { - return { ...s, offset: 1, cursorX: 2, selStart: 3, selEnd: 4 }; + TestRenderer.act(() => { + textInput.stateUpdater((s) => { + return { ...s, offset: 1, cursorX: 2, selStart: 3, selEnd: 4 }; + }); }); //then diff --git a/test/app/AppRoot.test.mjs b/test/app/AppRoot.test.mjs index e104591..171b0e6 100644 --- a/test/app/AppRoot.test.mjs +++ b/test/app/AppRoot.test.mjs @@ -115,7 +115,9 @@ describe("AppRoot.test.mjs", () => { assert.deepEqual(devToolProps.devTool, DevTool.Colors); //when - devToolProps.onActivate(DevTool.Logs); + TestRenderer.act(() => { + devToolProps.onActivate(DevTool.Logs); + }); //then assert.deepEqual(getDetToolProps().devTool, DevTool.Logs); @@ -182,12 +184,14 @@ describe("AppRoot.test.mjs", () => { assert.deepEqual(logCompProps.render("test log content"), null); //when - logCompProps.onReady(); + await TestRenderer.act(async () => { + logCompProps.onReady(); + }); //then - await Promise.resolve().then(() => { - assert.deepEqual(themeCtx.current, XTerm256Theme); - }); + await Promise.resolve(); + await Promise.resolve(); + assert.deepEqual(themeCtx.current, XTerm256Theme); assertComponents( renderer.root.children, h("box", { width: "100%" }, h(mainComp, null, h(taskControllerComp))), diff --git a/test/border/DoubleBorder.test.mjs b/test/border/DoubleBorder.test.mjs index 75bab8c..f379359 100644 --- a/test/border/DoubleBorder.test.mjs +++ b/test/border/DoubleBorder.test.mjs @@ -98,7 +98,7 @@ function assertDoubleBorder(result, props) { const left = props.left ?? 0; const top = props.top ?? 0; - const expected = /** @type {React.ReactElement[]} */ ([ + const expected = /** @type {readonly React.ReactElement[]} */ ([ h(horizontalLineComp, { left: left, top: top, diff --git a/test/menu/BottomMenuView.test.mjs b/test/menu/BottomMenuView.test.mjs index b8eee0c..29e5bb9 100644 --- a/test/menu/BottomMenuView.test.mjs +++ b/test/menu/BottomMenuView.test.mjs @@ -44,10 +44,11 @@ describe("BottomMenuView.test.mjs", () => { width: 80, items: new Array(12).fill("item"), }); - const clickables = /** @type {TestRenderer.ReactTestInstance[]} */ ( - TestRenderer.create(withThemeContext(h(BottomMenuView, props))).root - .children - ); + const clickables = + /** @type {readonly TestRenderer.ReactTestInstance[]} */ ( + TestRenderer.create(withThemeContext(h(BottomMenuView, props))).root + .children + ); //when clickables[0].props.onClick(); @@ -92,7 +93,7 @@ describe("BottomMenuView.test.mjs", () => { //then assert.deepEqual(BottomMenuView.displayName, "BottomMenuView"); - /** @type {BottomMenuViewItem[]} */ + /** @type {readonly BottomMenuViewItem[]} */ const itemsWithPos = [ { key: 1, item: " item ", pos: 0, textWidth: 8 }, { key: 2, item: " item ", pos: 8, textWidth: 8 }, diff --git a/test/menu/MenuBar.test.mjs b/test/menu/MenuBar.test.mjs index a3e1f09..daec2ff 100644 --- a/test/menu/MenuBar.test.mjs +++ b/test/menu/MenuBar.test.mjs @@ -32,7 +32,7 @@ MenuBar.buttonsPanel = mockComponent(ButtonsPanel); MenuBar.subMenuComp = mockComponent(SubMenu); const { popupComp, buttonsPanel, subMenuComp } = MenuBar; -/** @type {MenuBarItem[]} */ +/** @type {readonly MenuBarItem[]} */ const items = [ { label: "Menu 1", @@ -76,12 +76,16 @@ describe("MenuBar.test.mjs", () => { }); const renderer = TestRenderer.create(withThemeContext(h(MenuBar, props))); const buttonsProps = renderer.root.findByType(buttonsPanel).props; - buttonsProps.actions[0].onAction(); + TestRenderer.act(() => { + buttonsProps.actions[0].onAction(); + }); assert.deepEqual(renderer.root.findAllByType(subMenuComp).length, 1); const onKeypress = findOnKeypress(renderer); //when - assert.deepEqual(onKeypress("escape"), true); + TestRenderer.act(() => { + assert.deepEqual(onKeypress("escape"), true); + }); //then assert.deepEqual(renderer.root.findAllByType(subMenuComp).length, 0); @@ -111,7 +115,9 @@ describe("MenuBar.test.mjs", () => { }); const renderer = TestRenderer.create(withThemeContext(h(MenuBar, props))); const buttonsProps = renderer.root.findByType(buttonsPanel).props; - buttonsProps.actions[0].onAction(); + TestRenderer.act(() => { + buttonsProps.actions[0].onAction(); + }); assertComponent( renderer.root.findByType(subMenuComp), h(subMenuComp, { @@ -204,7 +210,9 @@ describe("MenuBar.test.mjs", () => { }); const renderer = TestRenderer.create(withThemeContext(h(MenuBar, props))); const buttonsProps = renderer.root.findByType(buttonsPanel).props; - buttonsProps.actions[0].onAction(); + TestRenderer.act(() => { + buttonsProps.actions[0].onAction(); + }); assert.deepEqual(renderer.root.findByType(subMenuComp).props.left, 2); //when & then @@ -263,7 +271,9 @@ describe("MenuBar.test.mjs", () => { }); const renderer = TestRenderer.create(withThemeContext(h(MenuBar, props))); const buttonsProps = renderer.root.findByType(buttonsPanel).props; - buttonsProps.actions[0].onAction(); + TestRenderer.act(() => { + buttonsProps.actions[0].onAction(); + }); assert.deepEqual(findOnKeypress(renderer)("down"), true); assert.deepEqual(renderer.root.findByType(subMenuComp).props.selected, 2); @@ -289,7 +299,9 @@ describe("MenuBar.test.mjs", () => { }); const renderer = TestRenderer.create(withThemeContext(h(MenuBar, props))); const buttonsProps = renderer.root.findByType(buttonsPanel).props; - buttonsProps.actions[0].onAction(); + TestRenderer.act(() => { + buttonsProps.actions[0].onAction(); + }); assert.deepEqual(renderer.root.findByType(subMenuComp).props.selected, 0); //when @@ -312,7 +324,9 @@ describe("MenuBar.test.mjs", () => { const buttonsProps = renderer.root.findByType(buttonsPanel).props; //when - buttonsProps.actions[0].onAction(); + TestRenderer.act(() => { + buttonsProps.actions[0].onAction(); + }); //then assertComponent( @@ -350,7 +364,14 @@ describe("MenuBar.test.mjs", () => { * @returns {(keyFull: string) => boolean} */ function findOnKeypress(renderer) { - return renderer.root.findByType(popupComp).props.onKeypress; + const onKeypress = renderer.root.findByType(popupComp).props.onKeypress; + return (keyFull) => { + let res = false; + TestRenderer.act(() => { + res = onKeypress(keyFull); + }); + return res; + }; } /** diff --git a/test/popup/ListPopup.test.mjs b/test/popup/ListPopup.test.mjs index ba7b79d..fbc5d0b 100644 --- a/test/popup/ListPopup.test.mjs +++ b/test/popup/ListPopup.test.mjs @@ -165,9 +165,9 @@ function getListPopupProps() { /** * @param {TestRenderer.ReactTestInstance} result * @param {ListPopupProps} props - * @param {string[]} items - * @param {number[]} screenSize - * @param {number[]} expectedSize + * @param {readonly string[]} items + * @param {readonly number[]} screenSize + * @param {readonly number[]} expectedSize */ function assertListPopup(result, props, items, screenSize, expectedSize) { assert.deepEqual(ListPopup.displayName, "ListPopup"); diff --git a/test/popup/MessageBox.test.mjs b/test/popup/MessageBox.test.mjs index d68cf25..735f9d7 100644 --- a/test/popup/MessageBox.test.mjs +++ b/test/popup/MessageBox.test.mjs @@ -241,7 +241,7 @@ function getMessageBoxProps() { /** * @param {TestRenderer.ReactTestInstance} result * @param {MessageBoxProps} props - * @param {string[]} actions + * @param {readonly string[]} actions * @param {boolean} closable */ function assertMessageBox(result, props, actions, closable = true) { diff --git a/test/popup/PopupOverlay.test.mjs b/test/popup/PopupOverlay.test.mjs index ad25e0e..379ff41 100644 --- a/test/popup/PopupOverlay.test.mjs +++ b/test/popup/PopupOverlay.test.mjs @@ -177,7 +177,7 @@ describe("PopupOverlay.test.mjs", () => { /** * @param {boolean} defaultPrevented * @param {boolean} handled - * @param {string[]} keys + * @param {readonly string[]} keys * @returns {void} */ function check(defaultPrevented, handled, ...keys) { diff --git a/test/portal/WithPortals.test.mjs b/test/portal/WithPortals.test.mjs index 04187a3..42c979b 100644 --- a/test/portal/WithPortals.test.mjs +++ b/test/portal/WithPortals.test.mjs @@ -42,7 +42,9 @@ describe("WithPortals.test.mjs", () => { ).root; //when & then - portalsCtx.current?.onRender(1, h(portalComp1)); + TestRenderer.act(() => { + portalsCtx.current?.onRender(1, h(portalComp1)); + }); assert.deepEqual(portalCtx1.current?.isActive, true); assertComponents( root.children, @@ -56,7 +58,9 @@ describe("WithPortals.test.mjs", () => { screenMock.focused = /** @type {BlessedElement} */ (focused2); //when & then - portalsCtx.current?.onRender(2, h(portalComp2)); + TestRenderer.act(() => { + portalsCtx.current?.onRender(2, h(portalComp2)); + }); assert.deepEqual(portalCtx1.current?.isActive, false); assert.deepEqual(portalCtx2.current?.isActive, true); assertComponents( @@ -83,15 +87,19 @@ describe("WithPortals.test.mjs", () => { h(React.Fragment, null, "some other content") ) ).root; - portalsCtx.current?.onRender(1, h(portalComp1)); - portalsCtx.current?.onRender(2, h(portalComp2)); + TestRenderer.act(() => { + portalsCtx.current?.onRender(1, h(portalComp1)); + portalsCtx.current?.onRender(2, h(portalComp2)); + }); assert.deepEqual(portalCtx1.current?.isActive, false); assert.deepEqual(portalCtx2.current?.isActive, true); const [updatedCtx1, updatedComp1] = getPortalCtxHook("updated content 1"); const [updatedCtx2, updatedComp2] = getPortalCtxHook("updated content 2"); //when & then - portalsCtx.current?.onRender(1, h(updatedComp1)); + TestRenderer.act(() => { + portalsCtx.current?.onRender(1, h(updatedComp1)); + }); assert.deepEqual(updatedCtx1.current?.isActive, false); assert.deepEqual(portalCtx2.current?.isActive, true); assertComponents( @@ -103,7 +111,9 @@ describe("WithPortals.test.mjs", () => { ); //when & then - portalsCtx.current?.onRender(2, h(updatedComp2)); + TestRenderer.act(() => { + portalsCtx.current?.onRender(2, h(updatedComp2)); + }); assert.deepEqual(updatedCtx1.current?.isActive, false); assert.deepEqual(updatedCtx2.current?.isActive, true); assertComponents( @@ -136,25 +146,33 @@ describe("WithPortals.test.mjs", () => { h(React.Fragment, null, "some other content") ) ).root; - portalsCtx.current?.onRender(0, h(portalComp0)); + TestRenderer.act(() => { + portalsCtx.current?.onRender(0, h(portalComp0)); + }); //given & when const focus1Mock = mockFunction(); const focused1 = { focus: () => focus1Mock() }; screenMock.focused = /** @type {BlessedElement} */ (focused1); - portalsCtx.current?.onRender(1, h(portalComp1)); + TestRenderer.act(() => { + portalsCtx.current?.onRender(1, h(portalComp1)); + }); //given & when const focus2Mock = mockFunction(); const focused2 = { focus: () => focus2Mock() }; screenMock.focused = /** @type {BlessedElement} */ (focused2); - portalsCtx.current?.onRender(2, h(portalComp2)); + TestRenderer.act(() => { + portalsCtx.current?.onRender(2, h(portalComp2)); + }); //given & when const focus3Mock = mockFunction(); const focused3 = { focus: () => focus3Mock() }; screenMock.focused = /** @type {BlessedElement} */ (focused3); - portalsCtx.current?.onRender(3, h(portalComp3)); + TestRenderer.act(() => { + portalsCtx.current?.onRender(3, h(portalComp3)); + }); //then assert.deepEqual(portalCtx0.current?.isActive, false); @@ -163,7 +181,9 @@ describe("WithPortals.test.mjs", () => { assert.deepEqual(portalCtx3.current?.isActive, true); //when & then - portalsCtx.current?.onRemove(3); + TestRenderer.act(() => { + portalsCtx.current?.onRemove(3); + }); assert.deepEqual(portalCtx1.current?.isActive, false); assert.deepEqual(portalCtx2.current?.isActive, true); assertComponents( @@ -176,7 +196,9 @@ describe("WithPortals.test.mjs", () => { ); //when & then - portalsCtx.current?.onRemove(1); + TestRenderer.act(() => { + portalsCtx.current?.onRemove(1); + }); assert.deepEqual(portalCtx2.current?.isActive, true); assertComponents( root.children, @@ -187,7 +209,9 @@ describe("WithPortals.test.mjs", () => { ); //when & then - portalsCtx.current?.onRemove(2); + TestRenderer.act(() => { + portalsCtx.current?.onRemove(2); + }); assert.deepEqual(portalCtx0.current?.isActive, true); assertComponents( root.children, @@ -197,7 +221,9 @@ describe("WithPortals.test.mjs", () => { ); //when & then - portalsCtx.current?.onRemove(0); + TestRenderer.act(() => { + portalsCtx.current?.onRemove(0); + }); assertComponents(root.children, h(portalsComp), "some other content"); //then diff --git a/test/task/TaskManager.test.mjs b/test/task/TaskManager.test.mjs index 3ae7368..b5300b0 100644 --- a/test/task/TaskManager.test.mjs +++ b/test/task/TaskManager.test.mjs @@ -7,6 +7,7 @@ import React from "react"; import TestRenderer from "react-test-renderer"; import { TestErrorBoundary, + actAsync, assertComponents, mockComponent, } from "react-assert"; @@ -65,22 +66,26 @@ describe("TaskManager.test.mjs", () => { ); }); - it("should set status to None when onHideStatus", () => { + it("should set status to None when onHideStatus", async () => { //given const task = Task("Fetching data", Promise.resolve()); const props = getTaskManagerProps(task); - const renderer = TestRenderer.create(h(TaskManager, props)); + const renderer = await actAsync(() => { + return TestRenderer.create(h(TaskManager, props)); + }); assertTaskManager(renderer.root, { - showLoading: true, - status: new RegExp(`^${task.message}\\.\\.\\.$`), + showLoading: false, + status: new RegExp(`^${task.message}\\.\\.\\.Done \\d+\\.\\d+ sec\\.$`), }); const uiProps = renderer.root.findByType(uiComponent).props; //when - uiProps.onHideStatus(); + TestRenderer.act(() => { + uiProps.onHideStatus(); + }); //then - assertTaskManager(renderer.root, { showLoading: true }); + assertTaskManager(renderer.root, { showLoading: false }); }); it("should log stack if js error and set error to undefined when onCloseErrorPopup", async () => { @@ -95,10 +100,12 @@ describe("TaskManager.test.mjs", () => { const rejected = Promise.reject(error); const task = Task("Fetching data", rejected); const props = getTaskManagerProps(task); - const renderer = TestRenderer.create(h(TaskManager, props)); + const renderer = await actAsync(() => { + return TestRenderer.create(h(TaskManager, props)); + }); assertTaskManager(renderer.root, { - showLoading: true, - status: new RegExp(`^${task.message}\\.\\.\\.$`), + showLoading: false, + status: new RegExp(`^${task.message}\\.\\.\\.Done \\d+\\.\\d+ sec\\.$`), }); await rejected.catch((error) => { @@ -114,7 +121,9 @@ describe("TaskManager.test.mjs", () => { const uiProps = renderer.root.findByType(uiComponent).props; //when - uiProps.onCloseErrorPopup(); + TestRenderer.act(() => { + uiProps.onCloseErrorPopup(); + }); //then assertTaskManager(renderer.root, { @@ -138,10 +147,12 @@ describe("TaskManager.test.mjs", () => { const props = getTaskManagerProps(task); //when - const renderer = TestRenderer.create(h(TaskManager, props)); + const renderer = await actAsync(() => { + return TestRenderer.create(h(TaskManager, props)); + }); assertTaskManager(renderer.root, { - showLoading: true, - status: new RegExp(`^${task.message}\\.\\.\\.$`), + showLoading: false, + status: new RegExp(`^${task.message}\\.\\.\\.Done \\d+\\.\\d+ sec\\.$`), }); await rejected.catch((error) => { @@ -167,7 +178,9 @@ describe("TaskManager.test.mjs", () => { const props = getTaskManagerProps(task1); //when & then - const renderer = TestRenderer.create(h(TaskManager, props)); + const renderer = await actAsync(() => { + return TestRenderer.create(h(TaskManager, props)); + }); assertTaskManager(renderer.root, { showLoading: true, status: new RegExp(`^${task1.message}\\.\\.\\.$`), @@ -192,7 +205,9 @@ describe("TaskManager.test.mjs", () => { //when & then assert.notDeepEqual(resolve1, undefined); resolve1?.apply(null, [undefined]); - await promise1; + await TestRenderer.act(async () => { + await promise1; + }); assertTaskManager(renderer.root, { showLoading: true, status: new RegExp(`^${task1.message}\\.\\.\\.Done \\d+\\.\\d+ sec\\.$`), @@ -201,7 +216,9 @@ describe("TaskManager.test.mjs", () => { //when & then assert.notDeepEqual(resolve2, undefined); resolve2?.apply(null, [undefined]); - await promise2; + await TestRenderer.act(async () => { + await promise2; + }); assertTaskManager(renderer.root, { showLoading: false, status: new RegExp(`^${task2.message}\\.\\.\\.Done \\d+\\.\\d+ sec\\.$`), diff --git a/test/task/TaskManagerUi.test.mjs b/test/task/TaskManagerUi.test.mjs index 322134e..eea771d 100644 --- a/test/task/TaskManagerUi.test.mjs +++ b/test/task/TaskManagerUi.test.mjs @@ -43,7 +43,9 @@ describe("TaskManagerUi.test.mjs", () => { const msgBox = renderer.root.findByType(messageBoxComp); //when - msgBox.props.actions[0].onAction(); + TestRenderer.act(() => { + msgBox.props.actions[0].onAction(); + }); //then assert.deepEqual(onCloseErrorPopup.times, 1); @@ -111,7 +113,9 @@ describe("TaskManagerUi.test.mjs", () => { ); //when & then - renderer.root.findByType(messageBoxComp).props.actions[0].onAction(); + TestRenderer.act(() => { + renderer.root.findByType(messageBoxComp).props.actions[0].onAction(); + }); assert.deepEqual(onCloseErrorPopup.times, 1); assert.deepEqual( renderer.root.findByType(messageBoxComp).props.message, @@ -119,7 +123,9 @@ describe("TaskManagerUi.test.mjs", () => { ); //when & then - renderer.root.findByType(messageBoxComp).props.actions[0].onAction(); + TestRenderer.act(() => { + renderer.root.findByType(messageBoxComp).props.actions[0].onAction(); + }); assert.deepEqual(onCloseErrorPopup.times, 2); assert.deepEqual(renderer.root.children.length, 0); }); diff --git a/test/tool/InputController.test.mjs b/test/tool/InputController.test.mjs index 8adb703..8817727 100644 --- a/test/tool/InputController.test.mjs +++ b/test/tool/InputController.test.mjs @@ -27,9 +27,11 @@ const g = /** @type {any} */ (global); * @param {string} msg */ const logKeys = (msg) => { - if (g.farjsLogKeys) { - g.farjsLogKeys(msg); - } + TestRenderer.act(() => { + if (g.farjsLogKeys) { + g.farjsLogKeys(msg); + } + }); }; describe("InputController.test.mjs", () => { diff --git a/types/Button.d.mts b/types/Button.d.mts index ad9b6a3..206802e 100644 --- a/types/Button.d.mts +++ b/types/Button.d.mts @@ -32,6 +32,6 @@ declare function Button(props: ButtonProps): React.DetailedReactHTMLElement<{ content: string; }, HTMLElement>; declare namespace Button { - const displayName: string; + let displayName: string; } import React from "react"; diff --git a/types/ButtonsPanel.d.mts b/types/ButtonsPanel.d.mts index 502efc3..67a7f9c 100644 --- a/types/ButtonsPanel.d.mts +++ b/types/ButtonsPanel.d.mts @@ -5,7 +5,7 @@ export type ButtonsPanelAction = { }; export type ButtonsPanelProps = { readonly top: number; - readonly actions: ButtonsPanelAction[]; + readonly actions: readonly ButtonsPanelAction[]; readonly style: import("@farjs/blessed").Widgets.Types.TStyle; readonly padding?: number; readonly margin?: number; @@ -19,7 +19,7 @@ export type ButtonsPanelProps = { /** * @typedef {{ * readonly top: number; - * readonly actions: ButtonsPanelAction[]; + * readonly actions: readonly ButtonsPanelAction[]; * readonly style: import("@farjs/blessed").Widgets.Types.TStyle; * readonly padding?: number; * readonly margin?: number; @@ -36,7 +36,7 @@ declare function ButtonsPanel(props: ButtonsPanelProps): React.ReactElement<{ style: import("blessed").Widgets.Types.TStyle; }, string | React.JSXElementConstructor>; declare namespace ButtonsPanel { - export const displayName: string; + export let displayName: string; export { Button as buttonComp }; } import React from "react"; diff --git a/types/CheckBox.d.mts b/types/CheckBox.d.mts index d8f52df..fe0b8ae 100644 --- a/types/CheckBox.d.mts +++ b/types/CheckBox.d.mts @@ -22,7 +22,7 @@ export type CheckBoxProps = { */ declare function CheckBox(props: CheckBoxProps): React.FunctionComponentElement<{}>; declare namespace CheckBox { - export const displayName: string; + export let displayName: string; export { Button as buttonComp }; } import React from "react"; diff --git a/types/ComboBox.d.mts b/types/ComboBox.d.mts index 60e386b..7d9b105 100644 --- a/types/ComboBox.d.mts +++ b/types/ComboBox.d.mts @@ -7,7 +7,7 @@ export type ComboBoxProps = { readonly left: number; readonly top: number; readonly width: number; - readonly items: string[]; + readonly items: readonly string[]; readonly value: string; onChange(value: string): void; onEnter?(): void; @@ -17,7 +17,7 @@ export type ComboBoxProps = { * readonly left: number; * readonly top: number; * readonly width: number; - * readonly items: string[]; + * readonly items: readonly string[]; * readonly value: string; * onChange(value: string): void; * onEnter?(): void; @@ -28,10 +28,10 @@ export type ComboBoxProps = { */ declare function ComboBox(props: ComboBoxProps): React.FunctionComponentElement<{}>; declare namespace ComboBox { - export const displayName: string; + export let displayName: string; export { TextInput as textInputComp }; export { ComboBoxPopup as comboBoxPopup }; - export const arrowDownCh: string; + export let arrowDownCh: string; } import React from "react"; import TextInput from "./TextInput.mjs"; diff --git a/types/ComboBoxPopup.d.mts b/types/ComboBoxPopup.d.mts index 6c76987..2c845be 100644 --- a/types/ComboBoxPopup.d.mts +++ b/types/ComboBoxPopup.d.mts @@ -4,7 +4,7 @@ export type ComboBoxPopupProps = { readonly left: number; readonly top: number; readonly width: number; - readonly items: string[]; + readonly items: readonly string[]; readonly style: import("@farjs/blessed").Widgets.Types.TStyle; readonly viewport: ListViewport; setViewport(viewport: ListViewport): void; @@ -15,7 +15,7 @@ export type ComboBoxPopupProps = { * readonly left: number; * readonly top: number; * readonly width: number; - * readonly items: string[]; + * readonly items: readonly string[]; * readonly style: import("@farjs/blessed").Widgets.Types.TStyle; * readonly viewport: ListViewport; * setViewport(viewport: ListViewport): void; @@ -37,11 +37,11 @@ declare function ComboBoxPopup(props: ComboBoxPopupProps): React.ReactElement<{ style: import("blessed").Widgets.Types.TStyle; }, string | React.JSXElementConstructor>; declare namespace ComboBoxPopup { - export const displayName: string; + export let displayName: string; export { SingleBorder as singleBorderComp }; export { ListView as listViewComp }; export { ScrollBar as scrollBarComp }; - export const maxItems: number; + export let maxItems: number; } import React from "react"; import SingleBorder from "./border/SingleBorder.mjs"; diff --git a/types/ListBox.d.mts b/types/ListBox.d.mts index 1c8972c..e7d5ad4 100644 --- a/types/ListBox.d.mts +++ b/types/ListBox.d.mts @@ -6,7 +6,7 @@ export type ListBoxProps = { readonly width: number; readonly height: number; readonly style: import("@farjs/blessed").Widgets.Types.TStyle; - readonly items: string[]; + readonly items: readonly string[]; readonly selected: number; onAction(index: number): void; onSelect?(index: number): void; @@ -18,7 +18,7 @@ export type ListBoxProps = { * readonly width: number; * readonly height: number; * readonly style: import("@farjs/blessed").Widgets.Types.TStyle; - * readonly items: string[]; + * readonly items: readonly string[]; * readonly selected: number; * onAction(index: number): void; * onSelect?(index: number): void; @@ -35,7 +35,7 @@ declare function ListBox(props: ListBoxProps): React.ReactElement<{ onKeypress: (ch: any, key: IKeyEventArg) => void; }, string | React.JSXElementConstructor>; declare namespace ListBox { - export const displayName: string; + export let displayName: string; export { ListView as listViewComp }; export { ScrollBar as scrollBarComp }; } diff --git a/types/ListView.d.mts b/types/ListView.d.mts index 62f07f4..8aec00a 100644 --- a/types/ListView.d.mts +++ b/types/ListView.d.mts @@ -4,7 +4,7 @@ export type ListViewProps = { readonly top: number; readonly width: number; readonly height: number; - readonly items: string[]; + readonly items: readonly string[]; readonly style: BlessedStyle; readonly viewport: ListViewport; setViewport(viewport: ListViewport): void; @@ -35,6 +35,6 @@ declare function ListView(props: ListViewProps): React.ReactElement<{ onClick: (data: MouseEvent) => void; }, string | React.JSXElementConstructor>; declare namespace ListView { - const displayName: string; + let displayName: string; } import React from "react"; diff --git a/types/ProgressBar.d.mts b/types/ProgressBar.d.mts index c7f1e04..d54788f 100644 --- a/types/ProgressBar.d.mts +++ b/types/ProgressBar.d.mts @@ -27,8 +27,8 @@ declare function ProgressBar(props: ProgressBarProps): React.ReactElement<{ content: string; }, string | React.JSXElementConstructor>; declare namespace ProgressBar { - const displayName: string; - const filledCh: string; - const dottedCh: string; + let displayName: string; + let filledCh: string; + let dottedCh: string; } import React from "react"; diff --git a/types/ScrollBar.d.mts b/types/ScrollBar.d.mts index e343f1a..ff4e63f 100644 --- a/types/ScrollBar.d.mts +++ b/types/ScrollBar.d.mts @@ -28,10 +28,10 @@ export type ScrollBarProps = { */ declare function ScrollBar(props: ScrollBarProps): React.FunctionComponentElement<{}>; declare namespace ScrollBar { - const displayName: string; - const markerCh: string; - const scrollCh: string; - const upArrowCh: string; - const downArrowCh: string; + let displayName: string; + let markerCh: string; + let scrollCh: string; + let upArrowCh: string; + let downArrowCh: string; } import React from "react"; diff --git a/types/TextBox.d.mts b/types/TextBox.d.mts index 3a9f0ed..2f27078 100644 --- a/types/TextBox.d.mts +++ b/types/TextBox.d.mts @@ -22,9 +22,19 @@ export type TextBoxProps = { /** * @param {TextBoxProps} props */ -declare function TextBox(props: TextBoxProps): React.FunctionComponentElement; +declare function TextBox(props: TextBoxProps): React.FunctionComponentElement<{ + inputRef: React.MutableRefObject; + left: number; + top: number; + width: number; + value: string; + state: import("./TextInput.mjs").TextInputState; + stateUpdater: React.Dispatch>; + onChange: (value: string) => void; + onEnter: (() => void) | undefined; +}>; declare namespace TextBox { - export const displayName: string; + export let displayName: string; export { TextInput as textInputComp }; } import React from "react"; diff --git a/types/TextInput.d.mts b/types/TextInput.d.mts index 223ef2b..6fad765 100644 --- a/types/TextInput.d.mts +++ b/types/TextInput.d.mts @@ -72,7 +72,7 @@ declare function TextInput(props: TextInputProps): React.ReactElement<{ onKeypress: (ch: object | null | undefined, key: IKeyEventArg) => void; }, string | React.JSXElementConstructor>; declare namespace TextInput { - const displayName: string; + let displayName: string; /** * @returns {TextInputState} */ diff --git a/types/TextLine.d.mts b/types/TextLine.d.mts index 795685d..099d4a0 100644 --- a/types/TextLine.d.mts +++ b/types/TextLine.d.mts @@ -33,7 +33,7 @@ declare function TextLine(props: TextLineProps): React.ReactElement<{ content: string; }, string | React.JSXElementConstructor> | null; declare namespace TextLine { - const displayName: string; + let displayName: string; /** * @param {string} text * @param {number} width diff --git a/types/UI.d.mts b/types/UI.d.mts index ea11859..7c3bc53 100644 --- a/types/UI.d.mts +++ b/types/UI.d.mts @@ -15,6 +15,6 @@ export function renderText(isBold: boolean, fgColor: string, bgColor: string, te /** * @param {string} text * @param {number} maxLen - * @returns {string[]} + * @returns {readonly string[]} */ -export function splitText(text: string, maxLen: number): string[]; +export function splitText(text: string, maxLen: number): readonly string[]; diff --git a/types/WithSize.d.mts b/types/WithSize.d.mts index c25eca3..bc5c0c0 100644 --- a/types/WithSize.d.mts +++ b/types/WithSize.d.mts @@ -17,6 +17,6 @@ declare function WithSize(props: WithSizeProps): React.ReactElement<{ onResize: () => void; }, string | React.JSXElementConstructor>; declare namespace WithSize { - const displayName: string; + let displayName: string; } import React from "react"; diff --git a/types/app/AppRoot.d.mts b/types/app/AppRoot.d.mts index 0aa98a1..1c7b6f4 100644 --- a/types/app/AppRoot.d.mts +++ b/types/app/AppRoot.d.mts @@ -36,14 +36,12 @@ export type AppRootProps = { */ declare function AppRoot(props: AppRootProps): React.FunctionComponentElement>; declare namespace AppRoot { - export const displayName: string; + export let displayName: string; export { TaskManager as taskControllerComp }; export { LogController as logControllerComp }; export { DevToolPanel as devToolPanelComp }; } import React from "react"; -import Theme from "../theme/Theme.mjs"; -import DevTool from "../tool/DevTool.mjs"; import TaskManager from "../task/TaskManager.mjs"; import LogController from "../tool/LogController.mjs"; import DevToolPanel from "../tool/DevToolPanel.mjs"; diff --git a/types/border/DoubleBorder.d.mts b/types/border/DoubleBorder.d.mts index 68a69ab..8a999a0 100644 --- a/types/border/DoubleBorder.d.mts +++ b/types/border/DoubleBorder.d.mts @@ -24,7 +24,7 @@ export type DoubleBorderProps = { */ declare function DoubleBorder(props: DoubleBorderProps): React.FunctionComponentElement<{}>; declare namespace DoubleBorder { - export const displayName: string; + export let displayName: string; export { HorizontalLine as horizontalLineComp }; export { VerticalLine as verticalLineComp }; export { TextLine as textLineComp }; diff --git a/types/border/HorizontalLine.d.mts b/types/border/HorizontalLine.d.mts index af31872..fdf6e01 100644 --- a/types/border/HorizontalLine.d.mts +++ b/types/border/HorizontalLine.d.mts @@ -31,6 +31,6 @@ declare function HorizontalLine(props: HorizontalLineProps): React.ReactElement< content: string; }, string | React.JSXElementConstructor>; declare namespace HorizontalLine { - const displayName: string; + let displayName: string; } import React from "react"; diff --git a/types/border/SingleBorder.d.mts b/types/border/SingleBorder.d.mts index f6685a3..0582dc0 100644 --- a/types/border/SingleBorder.d.mts +++ b/types/border/SingleBorder.d.mts @@ -16,7 +16,7 @@ export type SingleBorderProps = { */ declare function SingleBorder(props: SingleBorderProps): React.FunctionComponentElement<{}>; declare namespace SingleBorder { - export const displayName: string; + export let displayName: string; export { HorizontalLine as horizontalLineComp }; export { VerticalLine as verticalLineComp }; } diff --git a/types/border/VerticalLine.d.mts b/types/border/VerticalLine.d.mts index 2373203..3622f3a 100644 --- a/types/border/VerticalLine.d.mts +++ b/types/border/VerticalLine.d.mts @@ -31,6 +31,6 @@ declare function VerticalLine(props: VerticalLineProps): React.ReactElement<{ content: string; }, string | React.JSXElementConstructor>; declare namespace VerticalLine { - const displayName: string; + let displayName: string; } import React from "react"; diff --git a/types/menu/BottomMenu.d.mts b/types/menu/BottomMenu.d.mts index e23bf96..4621ead 100644 --- a/types/menu/BottomMenu.d.mts +++ b/types/menu/BottomMenu.d.mts @@ -1,18 +1,23 @@ export default BottomMenu; export type BottomMenuProps = { - readonly items: string[]; + readonly items: readonly string[]; }; /** * @typedef {{ - * readonly items: string[]; + * readonly items: readonly string[]; * }} BottomMenuProps */ /** * @param {BottomMenuProps} props */ -declare function BottomMenu(props: BottomMenuProps): React.FunctionComponentElement; +declare function BottomMenu(props: BottomMenuProps): React.FunctionComponentElement<{ + render: (width: number) => React.FunctionComponentElement<{ + width: number; + items: readonly string[]; + }>; +}>; declare namespace BottomMenu { - export const displayName: string; + export let displayName: string; export { WithSize as withSizeComp }; export { BottomMenuView as bottomMenuViewComp }; } diff --git a/types/menu/BottomMenuView.d.mts b/types/menu/BottomMenuView.d.mts index 4f0e65d..ff50df2 100644 --- a/types/menu/BottomMenuView.d.mts +++ b/types/menu/BottomMenuView.d.mts @@ -1,7 +1,7 @@ export default BottomMenuView; export type BottomMenuViewProps = { readonly width: number; - readonly items: string[]; + readonly items: readonly string[]; }; export type BottomMenuViewItem = { readonly key: number; @@ -12,7 +12,7 @@ export type BottomMenuViewItem = { /** * @typedef {{ * readonly width: number; - * readonly items: string[]; + * readonly items: readonly string[]; * }} BottomMenuViewProps */ /** @@ -28,6 +28,6 @@ export type BottomMenuViewItem = { */ declare function BottomMenuView(props: BottomMenuViewProps): React.FunctionComponentElement<{}>; declare namespace BottomMenuView { - const displayName: string; + let displayName: string; } import React from "react"; diff --git a/types/menu/MenuBar.d.mts b/types/menu/MenuBar.d.mts index 349d912..2cd0787 100644 --- a/types/menu/MenuBar.d.mts +++ b/types/menu/MenuBar.d.mts @@ -2,10 +2,10 @@ export default MenuBar; export type ButtonsPanelAction = import("../../src/ButtonsPanel.mjs").ButtonsPanelAction; export type MenuBarItem = { readonly label: string; - readonly subItems: string[]; + readonly subItems: readonly string[]; }; export type MenuBarProps = { - readonly items: MenuBarItem[]; + readonly items: readonly MenuBarItem[]; onAction(menuIndex: number, subIndex: number): void; onClose(): void; }; @@ -16,12 +16,12 @@ export type SubMenuState = { /** * @typedef {{ * readonly label: string; - * readonly subItems: string[]; + * readonly subItems: readonly string[]; * }} MenuBarItem */ /** * @typedef {{ - * readonly items: MenuBarItem[]; + * readonly items: readonly MenuBarItem[]; * onAction(menuIndex: number, subIndex: number): void; * onClose(): void; * }} MenuBarProps @@ -37,7 +37,7 @@ export type SubMenuState = { */ declare function MenuBar(props: MenuBarProps): React.FunctionComponentElement<{}>; declare namespace MenuBar { - export const displayName: string; + export let displayName: string; export { Popup as popupComp }; export { ButtonsPanel as buttonsPanel }; export { SubMenu as subMenuComp }; diff --git a/types/menu/MenuBarTrigger.d.mts b/types/menu/MenuBarTrigger.d.mts index ac7778d..e395090 100644 --- a/types/menu/MenuBarTrigger.d.mts +++ b/types/menu/MenuBarTrigger.d.mts @@ -4,6 +4,6 @@ export default MenuBarTrigger; */ declare function MenuBarTrigger(): React.ReactElement; declare namespace MenuBarTrigger { - const displayName: string; + let displayName: string; } import React from "react"; diff --git a/types/menu/MenuPopup.d.mts b/types/menu/MenuPopup.d.mts index 9d854c5..f1fa643 100644 --- a/types/menu/MenuPopup.d.mts +++ b/types/menu/MenuPopup.d.mts @@ -1,7 +1,7 @@ export default MenuPopup; export type MenuPopupProps = { readonly title: string; - readonly items: string[]; + readonly items: readonly string[]; getLeft(width: number): string; onSelect(index: number): void; onClose(): void; @@ -9,7 +9,7 @@ export type MenuPopupProps = { /** * @typedef {{ * readonly title: string; - * readonly items: string[]; + * readonly items: readonly string[]; * getLeft(width: number): string; * onSelect(index: number): void; * onClose(): void; @@ -20,7 +20,7 @@ export type MenuPopupProps = { */ declare function MenuPopup(props: MenuPopupProps): React.FunctionComponentElement; declare namespace MenuPopup { - export const displayName: string; + export let displayName: string; export { Popup as popupComp }; export { ModalContent as modalContentComp }; export { Button as buttonComp }; diff --git a/types/menu/SubMenu.d.mts b/types/menu/SubMenu.d.mts index 9669e84..58ba1c6 100644 --- a/types/menu/SubMenu.d.mts +++ b/types/menu/SubMenu.d.mts @@ -1,7 +1,7 @@ export default SubMenu; export type SubMenuProps = { readonly selected: number; - readonly items: string[]; + readonly items: readonly string[]; readonly top: number; readonly left: number; onClick(index: number): void; @@ -9,7 +9,7 @@ export type SubMenuProps = { /** * @typedef {{ * readonly selected: number; - * readonly items: string[]; + * readonly items: readonly string[]; * readonly top: number; * readonly left: number; * onClick(index: number): void; @@ -29,10 +29,10 @@ declare function SubMenu(props: SubMenuProps): React.ReactElement<{ style: import("../theme/Theme.mjs").ThemeEffects; }, string | React.JSXElementConstructor>; declare namespace SubMenu { - export const displayName: string; + export let displayName: string; export { DoubleBorder as doubleBorderComp }; export { HorizontalLine as horizontalLineComp }; - export const separator: string; + export let separator: string; } import React from "react"; import DoubleBorder from "../border/DoubleBorder.mjs"; diff --git a/types/popup/ListPopup.d.mts b/types/popup/ListPopup.d.mts index bba133c..88114b6 100644 --- a/types/popup/ListPopup.d.mts +++ b/types/popup/ListPopup.d.mts @@ -2,21 +2,21 @@ export default ListPopup; export type BlessedPadding = import("./ModalContent.mjs").BlessedPadding; export type ListPopupProps = { readonly title: string; - readonly items: string[]; + readonly items: readonly string[]; onAction(index: number): void; onClose(): void; - readonly selected?: number | undefined; + readonly selected?: number; onSelect?(index: number): void; onKeypress?(keyFull: string): boolean; - readonly footer?: string | undefined; - readonly textPaddingLeft?: number | undefined; - readonly textPaddingRight?: number | undefined; - readonly itemWrapPrefixLen?: number | undefined; + readonly footer?: string; + readonly textPaddingLeft?: number; + readonly textPaddingRight?: number; + readonly itemWrapPrefixLen?: number; }; /** * @typedef {{ * readonly title: string; - * readonly items: string[]; + * readonly items: readonly string[]; * onAction(index: number): void; * onClose(): void; * readonly selected?: number; @@ -33,14 +33,14 @@ export type ListPopupProps = { */ declare function ListPopup(props: ListPopupProps): React.FunctionComponentElement; declare namespace ListPopup { - export const displayName: string; + export let displayName: string; export { Popup as popupComp }; export { ModalContent as modalContentComp }; export { WithSize as withSizeComp }; export { ListBox as listBoxComp }; - export const paddingHorizontal: number; - export const paddingVertical: number; - export const padding: BlessedPadding; + export let paddingHorizontal: number; + export let paddingVertical: number; + export let padding: BlessedPadding; } import React from "react"; import Popup from "./Popup.mjs"; diff --git a/types/popup/MessageBox.d.mts b/types/popup/MessageBox.d.mts index ef00eea..684c8a8 100644 --- a/types/popup/MessageBox.d.mts +++ b/types/popup/MessageBox.d.mts @@ -4,14 +4,14 @@ export type MessageBoxAction = import("./MessageBoxAction.mjs").MessageBoxAction export type MessageBoxProps = { readonly title: string; readonly message: string; - readonly actions: MessageBoxAction[]; + readonly actions: readonly MessageBoxAction[]; readonly style: import("@farjs/blessed").Widgets.Types.TStyle; }; /** * @typedef {{ * readonly title: string; * readonly message: string; - * readonly actions: MessageBoxAction[]; + * readonly actions: readonly MessageBoxAction[]; * readonly style: import("@farjs/blessed").Widgets.Types.TStyle; * }} MessageBoxProps */ @@ -20,7 +20,7 @@ export type MessageBoxProps = { */ declare function MessageBox(props: MessageBoxProps): React.FunctionComponentElement; declare namespace MessageBox { - export const displayName: string; + export let displayName: string; export { Popup as popupComp }; export { ModalContent as modalContentComp }; export { TextLine as textLineComp }; diff --git a/types/popup/MessageBoxAction.d.mts b/types/popup/MessageBoxAction.d.mts index fe71464..1917ddf 100644 --- a/types/popup/MessageBoxAction.d.mts +++ b/types/popup/MessageBoxAction.d.mts @@ -4,8 +4,21 @@ export type MessageBoxAction = { readonly triggeredOnClose: boolean; onAction(): void; }; +/** + * @typedef {{ + * readonly label: string; + * readonly triggeredOnClose: boolean; + * onAction(): void; + * }} MessageBoxAction + */ +/** + * @param {string} label + * @param {boolean} [triggeredOnClose] + * @returns {(onAction: () => void) => MessageBoxAction} + */ +declare function MessageBoxAction(label: string, triggeredOnClose?: boolean): (onAction: () => void) => MessageBoxAction; declare namespace MessageBoxAction { - function OK(onAction: () => void): MessageBoxAction; - function YES(onAction: () => void): MessageBoxAction; - function NO(onAction: () => void): MessageBoxAction; + let OK: (onAction: () => void) => MessageBoxAction; + let YES: (onAction: () => void) => MessageBoxAction; + let NO: (onAction: () => void) => MessageBoxAction; } diff --git a/types/popup/Modal.d.mts b/types/popup/Modal.d.mts index 5586efa..64e9436 100644 --- a/types/popup/Modal.d.mts +++ b/types/popup/Modal.d.mts @@ -20,7 +20,7 @@ export type ModalProps = { */ declare function Modal(props: React.PropsWithChildren): React.FunctionComponentElement; declare namespace Modal { - export const displayName: string; + export let displayName: string; export { Popup as popupComp }; export { ModalContent as modalContentComp }; } diff --git a/types/popup/ModalContent.d.mts b/types/popup/ModalContent.d.mts index d8deb84..5038168 100644 --- a/types/popup/ModalContent.d.mts +++ b/types/popup/ModalContent.d.mts @@ -48,11 +48,11 @@ declare function ModalContent(props: React.PropsWithChildren) style: import("blessed").Widgets.Types.TStyle; }, string | React.JSXElementConstructor>; declare namespace ModalContent { - export const displayName: string; + export let displayName: string; export { DoubleBorder as doubleBorderComp }; - export const paddingHorizontal: number; - export const paddingVertical: number; - export const padding: BlessedPadding; + export let paddingHorizontal: number; + export let paddingVertical: number; + export let padding: BlessedPadding; } import React from "react"; import DoubleBorder from "../border/DoubleBorder.mjs"; diff --git a/types/popup/Popup.d.mts b/types/popup/Popup.d.mts index 91959e6..d566ec0 100644 --- a/types/popup/Popup.d.mts +++ b/types/popup/Popup.d.mts @@ -1,6 +1,6 @@ export default Popup; export type PopupProps = { - readonly focusable?: boolean | undefined; + readonly focusable?: boolean; onClose?(): void; onOpen?(): void; onKeypress?(keyFull: string): boolean; @@ -18,7 +18,7 @@ export type PopupProps = { */ declare function Popup(props: React.PropsWithChildren): React.FunctionComponentElement<{}>; declare namespace Popup { - export const displayName: string; + export let displayName: string; export { Portal as portalComp }; export { PopupOverlay as popupOverlayComp }; } diff --git a/types/popup/PopupOverlay.d.mts b/types/popup/PopupOverlay.d.mts index 5b1578a..a09e051 100644 --- a/types/popup/PopupOverlay.d.mts +++ b/types/popup/PopupOverlay.d.mts @@ -14,7 +14,7 @@ export type PopupProps = import("./Popup.mjs").PopupProps; */ declare function PopupOverlay(props: React.PropsWithChildren): React.ReactElement; declare namespace PopupOverlay { - const displayName: string; - const style: import("@farjs/blessed").Widgets.Types.TStyle; + let displayName: string; + let style: import("@farjs/blessed").Widgets.Types.TStyle; } import React from "react"; diff --git a/types/popup/StatusPopup.d.mts b/types/popup/StatusPopup.d.mts index a0422e3..7246913 100644 --- a/types/popup/StatusPopup.d.mts +++ b/types/popup/StatusPopup.d.mts @@ -16,7 +16,7 @@ export type StatusPopupProps = { */ declare function StatusPopup(props: StatusPopupProps): React.FunctionComponentElement; declare namespace StatusPopup { - export const displayName: string; + export let displayName: string; export { Popup as popupComp }; export { ModalContent as modalContentComp }; export { TextLine as textLineComp }; diff --git a/types/portal/Portal.d.mts b/types/portal/Portal.d.mts index 2702b9f..51887aa 100644 --- a/types/portal/Portal.d.mts +++ b/types/portal/Portal.d.mts @@ -10,8 +10,8 @@ export type PortalContext = { */ declare function Portal(props: React.PropsWithChildren<{}>): null; declare namespace Portal { - const displayName: string; - const Context: React.Context; - const _nextPortalId: number; + let displayName: string; + let Context: React.Context; + let _nextPortalId: number; } import React from "react"; diff --git a/types/portal/WithPortals.d.mts b/types/portal/WithPortals.d.mts index 6bb37ae..3dbbc14 100644 --- a/types/portal/WithPortals.d.mts +++ b/types/portal/WithPortals.d.mts @@ -12,11 +12,9 @@ export type WithPortalsContext = { onRemove(portalId: number): void; }; declare namespace WithPortals { - const Context: React.Context; - function create(screen: import("blessed").Widgets.Screen): { - (props: { - children?: React.ReactNode; - }): React.FunctionComponentElement<{}>; + let Context: React.Context; + function create(screen: BlessedScreen): { + (props: React.PropsWithChildren<{}>): React.FunctionComponentElement<{}>; displayName: string; }; } diff --git a/types/task/TaskManager.d.mts b/types/task/TaskManager.d.mts index 3923f63..dda498e 100644 --- a/types/task/TaskManager.d.mts +++ b/types/task/TaskManager.d.mts @@ -35,7 +35,7 @@ export type TaskError = { */ declare function TaskManager(props: TaskManagerProps): React.ReactElement>; declare namespace TaskManager { - export const displayName: string; + export let displayName: string; export { TaskManagerUi as uiComponent }; export function errorHandler(error: any): TaskError; } diff --git a/types/task/TaskManagerUi.d.mts b/types/task/TaskManagerUi.d.mts index 7af55bb..d53c6f8 100644 --- a/types/task/TaskManagerUi.d.mts +++ b/types/task/TaskManagerUi.d.mts @@ -12,7 +12,7 @@ export type TaskManagerUiProps = { */ declare function TaskManagerUi(props: TaskManagerUiProps): React.FunctionComponentElement<{}>; declare namespace TaskManagerUi { - export const displayName: string; + export let displayName: string; export { StatusPopup as statusPopupComp }; export { MessageBox as messageBoxComp }; } diff --git a/types/theme/Theme.d.mts b/types/theme/Theme.d.mts index 2bcad57..67dcdb2 100644 --- a/types/theme/Theme.d.mts +++ b/types/theme/Theme.d.mts @@ -26,7 +26,7 @@ export type ThemeTextBox = { readonly selected: ThemeEffects; }; declare namespace Theme { - const Context: React.Context; + let Context: React.Context; function useTheme(): Theme; } import React from "react"; diff --git a/types/tool/ColorPanel.d.mts b/types/tool/ColorPanel.d.mts index 86f17f0..5bfb79f 100644 --- a/types/tool/ColorPanel.d.mts +++ b/types/tool/ColorPanel.d.mts @@ -9,6 +9,6 @@ declare function ColorPanel(): React.ReactElement<{ content: string; }, string | React.JSXElementConstructor>; declare namespace ColorPanel { - const displayName: string; + let displayName: string; } import React from "react"; diff --git a/types/tool/DevToolPanel.d.mts b/types/tool/DevToolPanel.d.mts index 08814c4..3d2269d 100644 --- a/types/tool/DevToolPanel.d.mts +++ b/types/tool/DevToolPanel.d.mts @@ -9,7 +9,7 @@ export type DevToolPanelProps = { */ declare function DevToolPanel(props: DevToolPanelProps): React.FunctionComponentElement<{}>; declare namespace DevToolPanel { - export const displayName: string; + export let displayName: string; export { LogPanel as logPanelComp }; export { InputController as inputController }; export { ColorPanel as colorPanelComp }; diff --git a/types/tool/InputController.d.mts b/types/tool/InputController.d.mts index 7d8a1bd..2caf96b 100644 --- a/types/tool/InputController.d.mts +++ b/types/tool/InputController.d.mts @@ -3,9 +3,9 @@ declare function InputController(): React.FunctionComponentElement<{ content: string; }>; declare namespace InputController { - export const displayName: string; + export let displayName: string; export { LogPanel as logPanelComp }; - export const maxBufferLength: number; + export let maxBufferLength: number; } import React from "react"; import LogPanel from "./LogPanel.mjs"; diff --git a/types/tool/LogController.d.mts b/types/tool/LogController.d.mts index e7b7dbc..618ac8f 100644 --- a/types/tool/LogController.d.mts +++ b/types/tool/LogController.d.mts @@ -14,6 +14,6 @@ export type LogControllerProps = { */ declare function LogController(props: LogControllerProps): import("react").ReactElement> | null; declare namespace LogController { - const displayName: string; - const maxBufferLength: number; + let displayName: string; + let maxBufferLength: number; } diff --git a/types/tool/LogPanel.d.mts b/types/tool/LogPanel.d.mts index cc6ce10..7cc66db 100644 --- a/types/tool/LogPanel.d.mts +++ b/types/tool/LogPanel.d.mts @@ -15,6 +15,6 @@ declare function LogPanel(props: LogPanelProps): React.ReactElement<{ content: string; }, string | React.JSXElementConstructor>; declare namespace LogPanel { - const displayName: string; + let displayName: string; } import React from "react";