diff --git a/apis/nucleus/src/__tests__/nucleus.test.js b/apis/nucleus/src/__tests__/nucleus.test.js index 5a9ccace9..657544c32 100644 --- a/apis/nucleus/src/__tests__/nucleus.test.js +++ b/apis/nucleus/src/__tests__/nucleus.test.js @@ -1,4 +1,5 @@ -import Nuked, { getOptions } from '../index'; +import Nuked from '../index'; +import { getOptions } from '../components/listbox/ListBoxPortal'; import * as appLocaleModule from '../locale/app-locale'; import * as NebulaAppModule from '../components/NebulaApp'; import * as AppSelectionsModule from '../components/selections/AppSelections'; diff --git a/apis/nucleus/src/components/listbox/ListBoxPopoverWrapper.jsx b/apis/nucleus/src/components/listbox/ListBoxPopoverWrapper.jsx index 59d50a856..3f8d81a60 100644 --- a/apis/nucleus/src/components/listbox/ListBoxPopoverWrapper.jsx +++ b/apis/nucleus/src/components/listbox/ListBoxPopoverWrapper.jsx @@ -1,15 +1,28 @@ import React, { useState } from 'react'; +import extend from 'extend'; import ListBoxPopover from './ListBoxPopover'; -export default function ListBoxPopoverWrapper({ - app, - fieldIdentifier, - stateName, - element, - anchorOrigin, - transformOrigin, - options = {}, -}) { +const DEFAULTS = { + show: true, + anchorOrigin: { + vertical: 'bottom', + horizontal: 'center', + }, + transformOrigin: { + vertical: 'top', + horizontal: 'center', + }, +}; + +export const getOptions = (usersOptions = {}) => { + const squashedOptions = { + ...DEFAULTS, + }; + extend(true, squashedOptions, usersOptions); + return squashedOptions; +}; + +export default function ListBoxPopoverWrapper({ app, fieldIdentifier, stateName, element, options = {} }) { const [showState, setShowstate] = useState(!!options.show); const handleCloseShowState = () => { setShowstate(false); @@ -20,8 +33,8 @@ export default function ListBoxPopoverWrapper({ show={showState} app={app} alignTo={{ current: element }} - anchorOrigin={anchorOrigin} - transformOrigin={transformOrigin} + anchorOrigin={options.anchorOrigin} + transformOrigin={options.transformOrigin} close={handleCloseShowState} fieldName={fieldIdentifier} stateName={stateName} diff --git a/apis/nucleus/src/components/listbox/ListBoxPortal.jsx b/apis/nucleus/src/components/listbox/ListBoxPortal.jsx index da6b92373..563368e36 100644 --- a/apis/nucleus/src/components/listbox/ListBoxPortal.jsx +++ b/apis/nucleus/src/components/listbox/ListBoxPortal.jsx @@ -7,6 +7,52 @@ import useOnTheFlyModel from './hooks/useOnTheFlyModel'; import identify from './assets/identify'; import uid from '../../object/uid'; +/** + * @ignore + * @typedef {object} DoNotUseOptions Options strictly recommended not to use as they might change anytime. Documenting them to keep track of them, but not exposing them to API docs. + * @property {boolean=} [focusSearch=false] Initialize the Listbox with the search input focused. Only applicable when + * search is true, since toggling will always focus the search input on show. + * @property {boolean=} [options.showGray=true] Render fields or checkboxes in shades of gray instead of white when their state is excluded or alternative. + * @property {boolean=} [options.calculatePagesHeight=false] Override each page's qHeight with its actual row count. + * @property {object} [options.sessionModel] Use a custom sessionModel. + * @property {object} [options.selectionsApi] Use a custom selectionsApi to customize how values are selected. + * @property {function():boolean} [options.selectDisabled=] Define a function which tells when selections are disabled (true) or enabled (false). By default, always returns false. + * @property {function():object[]} [options.postProcessPages] A function for client-side post-processing of returned pages. + * @property {PromiseFunction} [options.fetchStart] A function called when the Listbox starts fetching data. Receives the fetch request promise as an argument. + * @property {ReceiverFunction} [options.update] A function which receives an update function which upon call will trigger a data fetch. + * @property {{setScrollPos:function(number):void, initScrollPos:number}} [options.scrollState=] Object including a setScrollPos function that sets current scroll position index. A initial scroll position index. + * @property {function(number):void} [options.setCount=] A function that gets called with the length of the data in the Listbox. + */ + +/** + * @ignore + * @param {object} usersOptions Options sent in to fieldInstance.mount. + * @param {DoNotUseOptions} __DO_NOT_USE__ + * @returns {object} Squashed options with defaults given for non-exposed options. + */ +export const getOptions = (usersOptions = {}) => { + const { __DO_NOT_USE__ = {}, ...exposedOptions } = usersOptions; + + const DO_NOT_USE_DEFAULTS = { + update: undefined, + fetchStart: undefined, + showGray: true, + focusSearch: false, + sessionModel: undefined, + selectionsApi: undefined, + selectDisabled: undefined, + postProcessPages: undefined, + calculatePagesHeight: false, + }; + const squashedOptions = { + ...exposedOptions, + ...DO_NOT_USE_DEFAULTS, + // eslint-disable-next-line no-underscore-dangle + ...__DO_NOT_USE__, + }; + return squashedOptions; +}; + function ListBoxWrapper({ app, fieldIdentifier, qId, stateName, element, options }) { const { isExistingObject, hasExternalSelectionsApi } = identify({ qId, options }); const [changeCount, setChangeCount] = useState(0); diff --git a/apis/nucleus/src/index.js b/apis/nucleus/src/index.js index 7e10cf969..966f40a1b 100644 --- a/apis/nucleus/src/index.js +++ b/apis/nucleus/src/index.js @@ -6,8 +6,10 @@ import deviceTypeFn from './device-type'; import bootNebulaApp from './components/NebulaApp'; import AppSelectionsPortal from './components/selections/AppSelections'; -import ListBoxPortal from './components/listbox/ListBoxPortal'; -import ListBoxPopoverWrapper from './components/listbox/ListBoxPopoverWrapper'; +import ListBoxPortal, { getOptions as getListboxPortalOptions } from './components/listbox/ListBoxPortal'; +import ListBoxPopoverWrapper, { + getOptions as getListboxPopoverOptions, +} from './components/listbox/ListBoxPopoverWrapper'; import create from './object/create-session-object'; import get from './object/get-generic-object'; @@ -124,52 +126,6 @@ const mergeConfigs = (base, c) => ({ * @typedef {function(function)} ReceiverFunction A callback function which receives another function as input. */ -/** - * @ignore - * @typedef {object} DoNotUseOptions Options strictly recommended not to use as they might change anytime. Documenting them to keep track of them, but not exposing them to API docs. - * @property {boolean=} [focusSearch=false] Initialize the Listbox with the search input focused. Only applicable when - * search is true, since toggling will always focus the search input on show. - * @property {boolean=} [options.showGray=true] Render fields or checkboxes in shades of gray instead of white when their state is excluded or alternative. - * @property {boolean=} [options.calculatePagesHeight=false] Override each page's qHeight with its actual row count. - * @property {object} [options.sessionModel] Use a custom sessionModel. - * @property {object} [options.selectionsApi] Use a custom selectionsApi to customize how values are selected. - * @property {function():boolean} [options.selectDisabled=] Define a function which tells when selections are disabled (true) or enabled (false). By default, always returns false. - * @property {function():object[]} [options.postProcessPages] A function for client-side post-processing of returned pages. - * @property {PromiseFunction} [options.fetchStart] A function called when the Listbox starts fetching data. Receives the fetch request promise as an argument. - * @property {ReceiverFunction} [options.update] A function which receives an update function which upon call will trigger a data fetch. - * @property {{setScrollPos:function(number):void, initScrollPos:number}} [options.scrollState=] Object including a setScrollPos function that sets current scroll position index. A initial scroll position index. - * @property {function(number):void} [options.setCount=] A function that gets called with the length of the data in the Listbox. - */ - -/** - * @ignore - * @param {object} usersOptions Options sent in to fieldInstance.mount. - * @param {DoNotUseOptions} __DO_NOT_USE__ - * @returns {object} Squashed options with defaults given for non-exposed options. - */ -export const getOptions = (usersOptions = {}) => { - const { __DO_NOT_USE__ = {}, ...exposedOptions } = usersOptions; - - const DO_NOT_USE_DEFAULTS = { - update: undefined, - fetchStart: undefined, - showGray: true, - focusSearch: false, - sessionModel: undefined, - selectionsApi: undefined, - selectDisabled: undefined, - postProcessPages: undefined, - calculatePagesHeight: false, - }; - const squashedOptions = { - ...exposedOptions, - ...DO_NOT_USE_DEFAULTS, - // eslint-disable-next-line no-underscore-dangle - ...__DO_NOT_USE__, - }; - return squashedOptions; -}; - function nuked(configuration = {}) { const locale = appLocaleFn(configuration.context.language); @@ -447,7 +403,7 @@ function nuked(configuration = {}) { app, fieldIdentifier, qId, - options: getOptions(options), + options: getListboxPortalOptions(options), stateName: options.stateName || '$', }); root.add(this._instance); @@ -485,19 +441,18 @@ function nuked(configuration = {}) { getRegisteredTypes: types.getList, __DO_NOT_USE__: { types, - popover(anchorElement, fieldIdentifier, options = { show: true }) { + popover(anchorElement, fieldIdentifier, options) { if (api._popoverInstance) { root.remove(api._popoverInstance); api._popoverInstance = null; } + const opts = getListboxPopoverOptions(options); api._popoverInstance = React.createElement(ListBoxPopoverWrapper, { element: anchorElement, key: uid(), app, - anchorOrigin: options.anchorOrigin, - transformOrigin: options.transformOrigin, fieldIdentifier, - options: getOptions({ ...options }), + options: opts, stateName: options.stateName || '$', }); root.add(api._popoverInstance);