Skip to content

Commit

Permalink
feat: add direction config to d2.config
Browse files Browse the repository at this point in the history
  • Loading branch information
KaiVandivier committed Jan 17, 2024
1 parent dc43300 commit b309510
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 35 deletions.
16 changes: 13 additions & 3 deletions adapter/src/components/AppWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ import { ErrorBoundary } from './ErrorBoundary.js'
import { LoadingMask } from './LoadingMask.js'
import { styles } from './styles/AppWrapper.style.js'

const AppWrapper = ({ children, plugin, onPluginError, clearPluginError }) => {
const { loading: localeLoading } = useCurrentUserLocale()
const AppWrapper = ({
children,
plugin,
onPluginError,
clearPluginError,
direction: configDirection,
}) => {
const { loading: localeLoading, direction: localeDirection } =
useCurrentUserLocale(configDirection)
const { loading: latestUserLoading } = useVerifyLatestUser()

if (localeLoading || latestUserLoading) {
Expand Down Expand Up @@ -40,7 +47,9 @@ const AppWrapper = ({ children, plugin, onPluginError, clearPluginError }) => {
return (
<div className="app-shell-adapter">
<style jsx>{styles}</style>
<ConnectedHeaderBar />
<div dir={localeDirection}>
<ConnectedHeaderBar />
</div>
<div className="app-shell-app">
<ErrorBoundary onRetry={() => window.location.reload()}>
{children}
Expand All @@ -54,6 +63,7 @@ const AppWrapper = ({ children, plugin, onPluginError, clearPluginError }) => {
AppWrapper.propTypes = {
children: PropTypes.node,
clearPluginError: PropTypes.func,
direction: PropTypes.oneOf(['ltr', 'rtl', 'auto']),
plugin: PropTypes.bool,
onPluginError: PropTypes.func,
}
Expand Down
3 changes: 3 additions & 0 deletions adapter/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const AppAdapter = ({
appVersion,
url,
apiVersion,
direction,
pwaEnabled,
plugin,
parentAlertsAdd,
Expand Down Expand Up @@ -41,6 +42,7 @@ const AppAdapter = ({
plugin={plugin}
onPluginError={onPluginError}
clearPluginError={clearPluginError}
direction={direction}
>
{children}
</AppWrapper>
Expand All @@ -56,6 +58,7 @@ AppAdapter.propTypes = {
apiVersion: PropTypes.number,
children: PropTypes.element,
clearPluginError: PropTypes.func,
direction: PropTypes.oneOf(['ltr', 'rtl', 'auto']),
parentAlertsAdd: PropTypes.func,
plugin: PropTypes.bool,
pwaEnabled: PropTypes.bool,
Expand Down
42 changes: 28 additions & 14 deletions adapter/src/utils/useLocale.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,40 @@ const setGlobalLocale = (locale) => {
}
moment.locale(locale)

// for i18n.dir, need JS-formatted locale
const jsLocale = transformJavaLocale(locale)
const direction = i18n.dir(jsLocale)
// set `dir` globally (then override in app wrapper if needed)
document.body.setAttribute('dir', direction)

const resolvedLocale = validateLocaleByBundle(locale)
i18n.changeLanguage(resolvedLocale)

console.log('🗺 Global d2-i18n locale initialized:', resolvedLocale)
}

export const useLocale = (locale) => {
// Sets the global direction based on the app's configured direction
// (which should be done to affect modals, alerts, and other portal elements),
// then returns the locale's direction for use on the header bar
const handleDirection = ({ locale, configDirection }) => {
// for i18n.dir, need JS-formatted locale
const jsLocale = transformJavaLocale(locale)
const localeDirection = i18n.dir(jsLocale)

const globalDirection =
configDirection === 'auto' ? localeDirection : configDirection
// set `dir` globally (then override in app wrapper if needed)
document.documentElement.setAttribute('dir', globalDirection)

return localeDirection
}

export const useLocale = ({ locale, configDirection }) => {
const [result, setResult] = useState({})

useEffect(() => {
if (!locale) {
return
}

const direction = handleDirection({ locale, configDirection })
setGlobalLocale(locale)
setResult(locale)
}, [locale])
setResult({ locale, direction })
}, [locale, configDirection])

return result
}
Expand All @@ -86,16 +97,19 @@ const settingsQuery = {
}
// note: userSettings.keyUiLocale is expected to be in the Java format,
// e.g. 'ar', 'ar_IQ', 'uz_UZ_Cyrl', etc.
export const useCurrentUserLocale = () => {
export const useCurrentUserLocale = (configDirection) => {
const { loading, error, data } = useDataQuery(settingsQuery)
const locale = useLocale(
data && (data.userSettings.keyUiLocale || window.navigator.language)
)
const { locale, direction } = useLocale({
locale:
data &&
(data.userSettings.keyUiLocale || window.navigator.language),
configDirection,
})

if (error) {
// This shouldn't happen, trigger the fatal error boundary
throw new Error('Failed to fetch user locale: ' + error)
}

return { loading: loading || !locale, locale }
return { loading: loading || !locale, locale, direction }
}
6 changes: 6 additions & 0 deletions cli/config/d2.config.app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
const config = {
type: 'app',

// sets the `dir` HTML attribute for the app:
// options are 'ltr', 'rtl', and 'auto'. If set to 'auto', the direction
// will be inferred by the user's UI locale.
// The header bar direction will always be set by the locale.
direction: 'ltr',

entryPoints: {
app: './src/App.js',
},
Expand Down
1 change: 1 addition & 0 deletions cli/src/lib/shell/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = ({ config, paths }) => {
const baseEnvVars = {
name: config.title,
version: config.version,
direction: config.direction,
}

return {
Expand Down
37 changes: 19 additions & 18 deletions docs/config/d2-config-js-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,25 @@ All properties are technically optional, but it is recommended to set them expli

The following configuration properties are supported:

| Property | Type | Default | Description |
| :--------------------: | :------------------: | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **type** | _string_ | **app** | Either **app** or **lib** |
| **name** | _string_ | `pkg.name` | A short, machine-readable unique name for this app |
| **title** | _string_ | `config.name` | The human-readable application title, which will appear in the HeaderBar |
| **id** | _string_ | | The ID of the app on the [App Hub](https://apps.dhis2.org/). Used when publishing the app to the App Hub with [d2 app scripts publish](../scripts/publish). See [this guide](https://developers.dhis2.org/docs/guides/publish-apphub/) to learn how to set up continuous delivery. |
| **description** | _string_ | `pkg.description` | A full-length description of the application |
| **author** | _string_ or _object_ | `pkg.author` | The name of the developer to include in the DHIS2 manifest, following [package.json author field syntax](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#people-fields-author-contributors). |
| **entryPoints.app** | _string_ | **./src/App** | The path to the application entrypoint (not used for libraries) |
| **entryPoints.plugin** | _string_ | | The path to the application's plugin entrypoint (not used for libraries) |
| **entryPoints.lib** | _string_ or _object_ | **./src/index** | The path to the library entrypoint(s) (not used for applications). Supports [conditional exports](https://nodejs.org/dist/latest-v16.x/docs/api/packages.html#packages_conditional_exports) |
| **dataStoreNamespace** | _string_ | | The DataStore and UserDataStore namespace to reserve for this application. The reserved namespace **must** be suitably unique, as other apps will fail to install if they attempt to reserve the same namespace - see the [webapp manifest docs](https://docs.dhis2.org/en/develop/loading-apps.html) |
| **customAuthorities** | _Array(string)_ | | An array of custom authorities to create when installing the app, these do not provide security protections in the DHIS2 REST API but can be assigned to user roles and used to modify the interface displayed to a user - see the [webapp manifest docs](https://docs.dhis2.org/en/develop/loading-apps.html) |
| **minDHIS2Version** | _string_ | | The minimum DHIS2 version the App supports (eg. '2.35'). Required when uploading an app to the App Hub. The app's major version in the app's package.json needs to be increased when changing this property. |
| **maxDHIS2Version** | _string_ | | The maximum DHIS2 version the App supports. |
| **coreApp** | _boolean_ | **false** | **ADVANCED** If true, build an app artifact to be included as a root-level core application |
| **standalone** | _boolean_ | **false** | **ADVANCED** If true, do NOT include a static BaseURL in the production app artifact. This includes the `Server` field in the login dialog, which is usually hidden and pre-configured in production. |
| **pwa** | _object_ | | **ADVANCED** Opts into and configures PWA settings for this app. Read more about the options in [the PWA docs](../pwa). |
| Property | Type | Default | Description |
| :--------------------: | :---------------------------: | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **type** | _string_ | **app** | Either **app** or **lib** |
| **name** | _string_ | `pkg.name` | A short, machine-readable unique name for this app |
| **title** | _string_ | `config.name` | The human-readable application title, which will appear in the HeaderBar |
| **direction** | `'ltr'`, `'rtl'`, or `'auto'` | `'ltr'` | Sets the `dir` HTML attribute on the `document` of the app. If set to `'auto'`, the direction will be inferred from the current user's UI locale setting. The header bar will always be considered 'auto' and is unaffected by this setting. |
| **id** | _string_ | | The ID of the app on the [App Hub](https://apps.dhis2.org/). Used when publishing the app to the App Hub with [d2 app scripts publish](../scripts/publish). See [this guide](https://developers.dhis2.org/docs/guides/publish-apphub/) to learn how to set up continuous delivery. |
| **description** | _string_ | `pkg.description` | A full-length description of the application |
| **author** | _string_ or _object_ | `pkg.author` | The name of the developer to include in the DHIS2 manifest, following [package.json author field syntax](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#people-fields-author-contributors). |
| **entryPoints.app** | _string_ | **./src/App** | The path to the application entrypoint (not used for libraries) |
| **entryPoints.plugin** | _string_ | | The path to the application's plugin entrypoint (not used for libraries) |
| **entryPoints.lib** | _string_ or _object_ | **./src/index** | The path to the library entrypoint(s) (not used for applications). Supports [conditional exports](https://nodejs.org/dist/latest-v16.x/docs/api/packages.html#packages_conditional_exports) |
| **dataStoreNamespace** | _string_ | | The DataStore and UserDataStore namespace to reserve for this application. The reserved namespace **must** be suitably unique, as other apps will fail to install if they attempt to reserve the same namespace - see the [webapp manifest docs](https://docs.dhis2.org/en/develop/loading-apps.html) |
| **customAuthorities** | _Array(string)_ | | An array of custom authorities to create when installing the app, these do not provide security protections in the DHIS2 REST API but can be assigned to user roles and used to modify the interface displayed to a user - see the [webapp manifest docs](https://docs.dhis2.org/en/develop/loading-apps.html) |
| **minDHIS2Version** | _string_ | | The minimum DHIS2 version the App supports (eg. '2.35'). Required when uploading an app to the App Hub. The app's major version in the app's package.json needs to be increased when changing this property. |
| **maxDHIS2Version** | _string_ | | The maximum DHIS2 version the App supports. |
| **coreApp** | _boolean_ | **false** | **ADVANCED** If true, build an app artifact to be included as a root-level core application |
| **standalone** | _boolean_ | **false** | **ADVANCED** If true, do NOT include a static BaseURL in the production app artifact. This includes the `Server` field in the login dialog, which is usually hidden and pre-configured in production. |
| **pwa** | _object_ | | **ADVANCED** Opts into and configures PWA settings for this app. Read more about the options in [the PWA docs](../pwa). |

> _Note_: Dynamic defaults above may reference `pkg` (a property of the local `package.json` file) or `config` (another property within `d2.config.js`).
Expand Down
1 change: 1 addition & 0 deletions examples/simple-app/d2.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const config = {
name: 'simple-app',
title: 'Simple Example App',
description: 'This is a simple example application',
direction: 'auto',

// standalone: true, // Don't bake-in a DHIS2 base URL, allow the user to choose

Expand Down
1 change: 1 addition & 0 deletions shell/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const appConfig = {
apiVersion: parseInt(process.env.REACT_APP_DHIS2_API_VERSION),
pwaEnabled: process.env.REACT_APP_DHIS2_APP_PWA_ENABLED === 'true',
plugin: isPlugin,
direction: process.env.REACT_APP_DHIS2_APP_DIRECTION,
}

const pluginConfig = {
Expand Down

0 comments on commit b309510

Please sign in to comment.