diff --git a/.prettierrc b/.prettierrc index 26f22541d..af6254e03 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,7 @@ { - "printWidth": 120, + "printWidth": 100, "singleQuote": true, - "trailingComma": "es5", + "trailingComma": "all", "semi": false, "overrides": [ { diff --git a/package.json b/package.json index 19971cfcb..43c8778e5 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,8 @@ "commander": "^5.1.0", "flat": "5.0.1", "i18next": "^21.9.2", + "i18next-browser-languagedetector": "^7.0.1", + "i18next-http-backend": "^2.1.1", "lodash": "^4.17.21", "ora": "^5.4.1", "prop-types": "^15.7.2", diff --git a/src/adminjs-options.interface.ts b/src/adminjs-options.interface.ts index ce8c55c0d..364f98051 100644 --- a/src/adminjs-options.interface.ts +++ b/src/adminjs-options.interface.ts @@ -84,7 +84,7 @@ export interface AdminJSOptions { * text: 'I am fetched from the backend', * } * }, - * component: 'SomeStats', + * component: 'CustomPage', * }, * anotherPage: { * label: "TypeScript page", @@ -240,7 +240,7 @@ export interface AdminJSOptions { * Check out the [i18n tutorial]{@tutorial i18n} to see how * internationalization in AdminJS works. */ - locale?: Locale; + locale?: Locale | ((admin?: CurrentAdmin) => Locale | Promise); /** * rollup bundle options; diff --git a/src/adminjs.ts b/src/adminjs.ts index e2dc2c01a..6e3d05892 100644 --- a/src/adminjs.ts +++ b/src/adminjs.ts @@ -1,8 +1,7 @@ import merge from 'lodash/merge' import * as path from 'path' import * as fs from 'fs' -import i18n, { i18n as I18n } from 'i18next' -import { FC } from 'react' +import type { FC } from 'react' import { AdminJSOptionsWithDefault, AdminJSOptions } from './adminjs-options.interface' import BaseResource from './backend/adapters/resource/base-resource' @@ -16,9 +15,9 @@ import { ACTIONS } from './backend/actions' import loginTemplate from './frontend/login-template' import { ListActionResponse } from './backend/actions/list/list-action' -import { combineTranslations, Locale } from './locale/config' -import { locales } from './locale' -import { TranslateFunctions, createFunctions } from './utils/translate-functions.factory' +import { defaultLocale } from './locale' +import { Locale } from './locale/config' +import { TranslateFunctions } from './utils/translate-functions.factory' import { relativeFilePathResolver } from './utils/file-resolver' import { getComponentHtml } from './backend/utils' import { ComponentLoader } from './backend/utils/component-loader' @@ -72,8 +71,6 @@ class AdminJS { public locale!: Locale - public i18n!: I18n - public translateFunctions!: TranslateFunctions public componentLoader: ComponentLoader @@ -117,7 +114,8 @@ class AdminJS { this.resolveBabelConfigPath() - this.initI18n() + // To be removed when Login page will be renedered on client side + this.locale = defaultLocale const { databases, resources } = this.options @@ -127,37 +125,6 @@ class AdminJS { this.resources = resourcesFactory.buildResources({ databases, resources }) } - initI18n(): void { - const language = this.options.locale?.language || locales.en.language - const defaultTranslations = locales[language]?.translations || locales.en.translations - const availableLanguages = this.options.locale?.availableLanguages || [language] - this.locale = { - translations: combineTranslations(defaultTranslations, this.options.locale?.translations), - language, - availableLanguages, - } - if (i18n.isInitialized) { - i18n.addResourceBundle(this.locale.language, 'translation', this.locale.translations) - } else { - i18n.init({ - lng: this.locale.language, - initImmediate: false, // loads translations immediately - resources: { - [this.locale.language]: { - translation: this.locale.translations, - }, - }, - }) - } - - // mixin translate functions to AdminJS instance so users will be able to - // call AdminJS.translateMessage(...) - this.translateFunctions = createFunctions(i18n) - Object.getOwnPropertyNames(this.translateFunctions).forEach((translateFunctionName) => { - this[translateFunctionName] = this.translateFunctions[translateFunctionName] - }) - } - /** * Registers various database adapters written for AdminJS. * diff --git a/src/backend/utils/custom-views/get-component-html.tsx b/src/backend/utils/custom-views/get-component-html.tsx index 04855978c..651e5754e 100644 --- a/src/backend/utils/custom-views/get-component-html.tsx +++ b/src/backend/utils/custom-views/get-component-html.tsx @@ -4,7 +4,6 @@ import { ServerStyleSheet, StyleSheetManager, ThemeProvider } from 'styled-compo import { Provider } from 'react-redux' import { I18nextProvider } from 'react-i18next' import { combineStyles } from '@adminjs/design-system' -import i18n from 'i18next' import { Store } from 'redux' import { getAssets, getBranding, getFaviconFromBranding } from '../../../backend/utils/options-parser/options-parser' @@ -16,6 +15,7 @@ import createStore, { ReduxState, } from '../../../frontend/store/store' import AdminJS from '../../../adminjs' +import initTranslations from '../../../frontend/utils/adminjs.i18n' export async function getComponentHtml>( Component: React.FC, @@ -41,27 +41,18 @@ export async function getComponentHtml>( const theme = combineStyles((branding && branding.theme) || {}) const { locale } = store.getState() - i18n - .init({ - resources: { - [locale.language]: { - translation: locale.translations, - }, - }, - lng: locale.language, - interpolation: { escapeValue: false }, - }) + const { i18n } = initTranslations(locale) const sheet = new ServerStyleSheet() const component = renderToString( - - + + - - + + , ) diff --git a/src/backend/utils/options-parser/options-parser.ts b/src/backend/utils/options-parser/options-parser.ts index f68980a86..721fd711b 100644 --- a/src/backend/utils/options-parser/options-parser.ts +++ b/src/backend/utils/options-parser/options-parser.ts @@ -2,6 +2,7 @@ import merge from 'lodash/merge' import AdminJS from '../../../adminjs' import { AdminJSOptions, Assets, BrandingOptions } from '../../../adminjs-options.interface' import { CurrentAdmin } from '../../../current-admin.interface' +import { defaultLocale, Locale } from '../../../locale' import ViewHelpers from '../view-helpers/view-helpers' const defaultBranding: AdminJSOptions['branding'] = { @@ -45,6 +46,18 @@ export const getBranding = async ( return merged } +export const getLocales = async ( + admin: AdminJS, + currentAdmin?: CurrentAdmin, +): Promise => { + const { locale } = admin.options || {} + const computed = typeof locale === 'function' + ? await locale(currentAdmin) + : locale + + return merge({}, defaultLocale, computed) +} + export const getFaviconFromBranding = (branding: BrandingOptions): string => { if (branding.favicon) { const { favicon } = branding diff --git a/src/frontend/bundle-entry.jsx b/src/frontend/bundle-entry.jsx index 23bfa937b..b8083eafb 100644 --- a/src/frontend/bundle-entry.jsx +++ b/src/frontend/bundle-entry.jsx @@ -1,21 +1,19 @@ -import React from 'react' +import React, { Suspense } from 'react' +import { I18nextProvider } from 'react-i18next' import { Provider } from 'react-redux' import { BrowserRouter } from 'react-router-dom' import { ThemeProvider } from 'styled-components' -import { initReactI18next } from 'react-i18next' -import i18n from 'i18next' -import merge from 'lodash/merge' -import App from './components/application' -import BasePropertyComponent, { CleanPropertyComponent } from './components/property-type' -import createStore from './store/store' import ViewHelpers from '../backend/utils/view-helpers/view-helpers' +import { flat } from '../utils/flat' import * as AppComponents from './components/app' +import App from './components/application' +import BasePropertyComponent, { CleanPropertyComponent } from './components/property-type' +import withNotice from './hoc/with-notice' import * as Hooks from './hooks' +import createStore from './store/store' import ApiClient from './utils/api-client' -import withNotice from './hoc/with-notice' -import { flat } from '../utils/flat' -import { locales } from '../locale' +import initTranslations from './utils/adminJS.i18n' const env = { NODE_ENV: process.env.NODE_ENV || 'development', @@ -23,34 +21,21 @@ const env = { const store = createStore(window.REDUX_STATE) const theme = window.THEME -const defaultLocale = window.REDUX_STATE.locale -const currentLocale = JSON.parse(window.localStorage.getItem('locale')) || defaultLocale.language - -i18n.use(initReactI18next).init({ - resources: Object.keys(locales).reduce( - (memo, locale) => ({ - ...memo, - [locale]: { - translation: merge( - locales[locale].translations, - locale === defaultLocale.language ? defaultLocale.translations : {}, - ), - }, - }), - {}, - ), - lng: currentLocale, - interpolation: { escapeValue: false }, -}) +const { locale } = store.getState() +const { i18n } = initTranslations(locale) const Application = ( - - - - - - - + + + + + + + + + + + ) // eslint-disable-next-line no-undef diff --git a/src/frontend/components/app/action-header/actions-to-button-group.spec.ts b/src/frontend/components/app/action-header/actions-to-button-group.spec.ts index 8832ecd02..9d8028dce 100644 --- a/src/frontend/components/app/action-header/actions-to-button-group.spec.ts +++ b/src/frontend/components/app/action-header/actions-to-button-group.spec.ts @@ -2,16 +2,16 @@ import { ButtonGroupProps } from '@adminjs/design-system' import { expect } from 'chai' import i18n from 'i18next' import factory from 'factory-girl' -import { ActionJSON } from '../../../interfaces' +import { ActionJSON, ModalFunctions } from '../../../interfaces' import { actionsToButtonGroup } from './actions-to-button-group' import { createFunctions } from '../../../../utils/translate-functions.factory' import '../../spec/action-json.factory' const translateFunctions = createFunctions(i18n) -const modalFunctions = { +const modalFunctions: ModalFunctions = { closeModal: () => { /* noop */ }, - openModal: (data) => { /* noop */ }, + openModal: () => { /* noop */ }, } describe('actionsToButtonGroup', () => { diff --git a/src/frontend/components/app/breadcrumbs.tsx b/src/frontend/components/app/breadcrumbs.tsx index a7e79fbff..4e2ebecc2 100644 --- a/src/frontend/components/app/breadcrumbs.tsx +++ b/src/frontend/components/app/breadcrumbs.tsx @@ -10,7 +10,7 @@ import { RecordJSON, ResourceJSON } from '../../interfaces' import { getActionElementCss } from '../../utils' export const BreadcrumbLink = styled(Link)` - color: ${({ theme }): string => theme.colors.grey100}; + color: ${({ theme }): string => theme.colors.grey60}; font-family: ${({ theme }): string => theme.font}; line-height: ${({ theme }): string => theme.lineHeights.default}; font-size: ${({ theme }): string => theme.fontSizes.default}; @@ -26,13 +26,14 @@ export const BreadcrumbLink = styled(Link)` } &:last-child { + color: ${({ theme }): string => theme.colors.text}; &:after { content: ''; } } ` -export const BreadcrumbText = styled(Text)` +export const BreadcrumbText = styled(Text)` color: ${({ theme }): string => theme.colors.grey100}; font-family: ${({ theme }): string => theme.font}; font-weight: ${({ theme }): string => theme.fontWeights.normal.toString()}; @@ -81,8 +82,9 @@ const Breadcrumbs: React.FC = (props) => { const listAction = resource.resourceActions.find(({ name }) => name === 'list') const action = resource.actions.find((a) => a.name === actionName) const h = new ViewHelpers() - const { translateLabel: tl } = useTranslation() + const { tl, ta } = useTranslation() const contentTag = getActionElementCss(resource.id, actionName, 'breadcrumbs') + return ( {tl('dashboard')} @@ -93,7 +95,7 @@ const Breadcrumbs: React.FC = (props) => { ) : ( {resource.name} )} - {action && action.name !== 'list' && {action.label}} + {action && action.name !== 'list' && {ta(action.label)}} ) } diff --git a/src/frontend/components/app/default-dashboard.tsx b/src/frontend/components/app/default-dashboard.tsx index 578fbdda0..252e5e074 100644 --- a/src/frontend/components/app/default-dashboard.tsx +++ b/src/frontend/components/app/default-dashboard.tsx @@ -1,15 +1,6 @@ import React from 'react' import styled from 'styled-components' -import { - Box, - H2, - H5, - H4, - Text, - Illustration, - IllustrationProps, - Button, -} from '@adminjs/design-system' +import { Box, Button, H2, H5, Illustration, IllustrationProps, Text } from '@adminjs/design-system' import { useTranslation } from '../../hooks' @@ -98,8 +89,11 @@ const boxes = ({ translateMessage }): Array => [{ const Card = styled(Box)` display: ${({ flex }): string => (flex ? 'flex' : 'block')}; color: ${({ theme }) => theme.colors.grey100}; + height: 100%; text-decoration: none; border: 1px solid transparent; + border-radius: ${({ theme }) => theme.space.md}; + transition: all 0.1s ease-in; &:hover { border: 1px solid ${({ theme }) => theme.colors.primary100}; box-shadow: ${({ theme }) => theme.shadows.cardHover}; @@ -147,7 +141,7 @@ export const Dashboard: React.FC = () => { -

{translateMessage('community_title')}

+
{translateMessage('community_title')}
{translateMessage('community_subtitle')}
@@ -156,15 +150,15 @@ export const Dashboard: React.FC = () => { -

{translateMessage('foundBug_title')}

+
{translateMessage('foundBug_title')}
{translateMessage('foundBug_subtitle')}
- + -

{translateMessage('needMoreSolutions_title')}

+
{translateMessage('needMoreSolutions_title')}
{translateMessage('needMoreSolutions_subtitle')}
-
+ ) diff --git a/src/frontend/components/app/error-message.tsx b/src/frontend/components/app/error-message.tsx index 58c9fe77c..639d89664 100644 --- a/src/frontend/components/app/error-message.tsx +++ b/src/frontend/components/app/error-message.tsx @@ -1,5 +1,5 @@ import React, { ReactNode } from 'react' -import { MessageBox, Text } from '@adminjs/design-system' +import { InfoBox, MessageBox, Text } from '@adminjs/design-system' import { useTranslation } from '../../hooks' /** @@ -36,20 +36,19 @@ const ErrorMessageBox: React.FC = (props) => { ) } -const NoResourceError: React.FC<{resourceId: string}> = (props) => { +const NoResourceError: React.FC<{ resourceId: string }> = (props) => { const { resourceId } = props const { translateMessage } = useTranslation() return ( - {translateMessage('error404Resource', resourceId, { resourceId })} - + ) } @@ -57,16 +56,15 @@ const NoActionError: React.FC<{ resourceId: string; actionName: string }> = (pro const { resourceId, actionName } = props const { translateMessage } = useTranslation() return ( - {translateMessage('error404Action', resourceId, { resourceId, actionName })} - + ) } @@ -77,16 +75,15 @@ const NoRecordError: React.FC<{ const { resourceId, recordId } = props const { translateMessage } = useTranslation() return ( - {translateMessage('error404Record', resourceId, { resourceId, recordId })} - + ) } diff --git a/src/frontend/components/app/filter-drawer.tsx b/src/frontend/components/app/filter-drawer.tsx index 9929c89e1..59f18e653 100644 --- a/src/frontend/components/app/filter-drawer.tsx +++ b/src/frontend/components/app/filter-drawer.tsx @@ -1,7 +1,7 @@ import { Box, Button, Drawer, DrawerContent, - DrawerFooter, Icon, + DrawerFooter, H3, Icon, } from '@adminjs/design-system' import React, { MouseEvent, SyntheticEvent, useEffect, useRef, useState } from 'react' import { useLocation, useNavigate, useParams } from 'react-router-dom' @@ -42,7 +42,7 @@ const FilterDrawer: React.FC = (props) => { const [filter, setFilter] = useState(parseQuery(location)) const params = useParams() const navigate = useNavigate() - const { translateButton } = useTranslation() + const { translateButton, translateLabel } = useTranslation() const initialLoad = useRef(true) useEffect(() => { @@ -103,16 +103,17 @@ const FilterDrawer: React.FC = (props) => { return ( - + +

{translateLabel('filters', resource.id)}

@@ -129,12 +130,10 @@ const FilterDrawer: React.FC = (props) => {
- - - - diff --git a/src/frontend/components/app/language-select/language-select.tsx b/src/frontend/components/app/language-select/language-select.tsx index 1f3e85da5..52f23a293 100644 --- a/src/frontend/components/app/language-select/language-select.tsx +++ b/src/frontend/components/app/language-select/language-select.tsx @@ -1,23 +1,31 @@ -import { Box, Button, DropDown, DropDownItem, DropDownMenu, DropDownTrigger, Icon } from '@adminjs/design-system' -import React, { FC, useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' -import { useLocalStorage } from '../../../hooks' -import { ReduxState } from '../../../store/store' +import { + Box, + Button, + DropDown, + DropDownItem, + DropDownMenu, + DropDownTrigger, + Icon, +} from '@adminjs/design-system' +import React, { FC, useMemo } from 'react' +import { useTranslation } from '../../../hooks' const LanguageSelect: FC = () => { - const locale = useSelector((state: ReduxState) => state.locale) - const [storedLocale, setStoredLocale] = useLocalStorage('locale', locale.language) - const { availableLanguages } = locale + const { + i18n: { + language, + options: { supportedLngs }, + changeLanguage, + }, + translateComponent, + } = useTranslation() - const { i18n } = useTranslation() - - const handleButtonClick = useCallback((lng: string) => { - i18n.changeLanguage(lng) - setStoredLocale(lng) - }, []) + const availableLanguages: readonly string[] = useMemo( + () => (supportedLngs ? supportedLngs.filter((lang) => lang !== 'cimode') : []), + [supportedLngs], + ) - if (!availableLanguages.length || availableLanguages.length === 1) { + if (availableLanguages.length <= 1) { return null } @@ -27,13 +35,18 @@ const LanguageSelect: FC = () => { {availableLanguages.map((lang) => ( - handleButtonClick(lang)}> - {lang} + { + changeLanguage(lang) + }} + > + {translateComponent(`LanguageSelector.availableLanguages.${lang}`, { defaultValue: lang })} ))} diff --git a/src/frontend/components/app/records-table/no-records.tsx b/src/frontend/components/app/records-table/no-records.tsx index 7de5ccb1c..02daf97fa 100644 --- a/src/frontend/components/app/records-table/no-records.tsx +++ b/src/frontend/components/app/records-table/no-records.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Text, Button, Icon, InfoBox, Illustration, H4 } from '@adminjs/design-system' +import { Text, Button, Icon, InfoBox } from '@adminjs/design-system' import { ResourceJSON } from '../../../interfaces' import { useTranslation } from '../../../hooks' @@ -17,20 +17,18 @@ const NoRecordsOriginal: React.FC = (props) => { const canCreate = resource.resourceActions.find((a) => a.name === 'new') return ( - - -

{translateMessage('noRecords', resource.id)}

+ {translateMessage('noRecordsInResource', resource.id)} - {canCreate ? ( + {canCreate && ( - ) : ''} + )} ) } diff --git a/src/frontend/components/app/records-table/property-header.spec.tsx b/src/frontend/components/app/records-table/property-header.spec.tsx index 61cc96850..cd065930a 100644 --- a/src/frontend/components/app/records-table/property-header.spec.tsx +++ b/src/frontend/components/app/records-table/property-header.spec.tsx @@ -40,12 +40,13 @@ describe('', function () { }) context('render not selected but searchable field', function () { - xit('renders a label', async function () { - const { findAllByText } = renderSubject(property, sortBy, direction) + it('renders a client side translated label', async function () { + const { findByText } = renderSubject(property, sortBy, direction) + const translatedLabel = 'Some Property 1' - const label = await findAllByText(property.label) + const label = await findByText(translatedLabel) - expect(label).to.equal(property.label) + expect(label).to.exist }) it('wraps it within a link with an opposite direction', function () { diff --git a/src/frontend/components/app/records-table/records-table.spec.tsx b/src/frontend/components/app/records-table/records-table.spec.tsx index c2cd028fd..f90914d2b 100644 --- a/src/frontend/components/app/records-table/records-table.spec.tsx +++ b/src/frontend/components/app/records-table/records-table.spec.tsx @@ -3,8 +3,6 @@ import { render, RenderResult } from '@testing-library/react' import sinon from 'sinon' import { expect } from 'chai' import factory from 'factory-girl' -import { I18nextProvider } from 'react-i18next' -import i18n from 'i18next' import { Provider } from 'react-redux' import { RecordsTable, RecordsTableProps } from './records-table' @@ -27,15 +25,13 @@ const renderSubject = (props: Omit - - - - - + + + , ) diff --git a/src/frontend/components/app/sidebar/sidebar-pages.tsx b/src/frontend/components/app/sidebar/sidebar-pages.tsx index ed619d20f..6fc69af0f 100644 --- a/src/frontend/components/app/sidebar/sidebar-pages.tsx +++ b/src/frontend/components/app/sidebar/sidebar-pages.tsx @@ -16,7 +16,7 @@ const h = new ViewHelpers() const SidebarPages: React.FC = (props) => { const { pages } = props - const { translateLabel } = useTranslation() + const { translateLabel, translatePage } = useTranslation() const location = useLocation() const navigate = useNavigate() @@ -30,7 +30,7 @@ const SidebarPages: React.FC = (props) => { const elements: Array = pages.map((page) => ({ id: page.name, - label: page.name, + label: translatePage(page.name), isSelected: isActive(page), icon: page.icon, href: h.pageUrl(page.name), diff --git a/src/frontend/components/login/index.tsx b/src/frontend/components/login/index.tsx index e20568d93..cfde98074 100644 --- a/src/frontend/components/login/index.tsx +++ b/src/frontend/components/login/index.tsx @@ -1,23 +1,14 @@ -import React from 'react' -import styled, { createGlobalStyle } from 'styled-components' - -import { useSelector } from 'react-redux' import { - Box, - H5, - H2, - Label, - Illustration, - Input, - FormGroup, - Button, - Text, - MessageBox, - MadeWithLove, - themeGet, + Box, Button, FormGroup, H2, H5, Illustration, + Input, Label, MadeWithLove, MessageBox, Text, } from '@adminjs/design-system' +import React from 'react' +import { I18nextProvider } from 'react-i18next' +import { useSelector } from 'react-redux' +import styled, { createGlobalStyle } from 'styled-components' import { useTranslation } from '../../hooks' import { ReduxState } from '../../store/store' +import initTranslations from '../../utils/adminjs.i18n' const GlobalStyle = createGlobalStyle` html, body, #app { @@ -37,7 +28,7 @@ const Wrapper = styled(Box)` const StyledLogo = styled.img` max-width: 200px; - margin: ${themeGet('space', 'md')} 0; + margin: ${({ theme }) => theme.space.md} 0; ` export type LoginProps = { @@ -49,9 +40,11 @@ export const Login: React.FC = (props) => { const { action, message } = props const { translateLabel, translateButton, translateProperty, translateMessage } = useTranslation() const branding = useSelector((state: ReduxState) => state.branding) + const locale = useSelector((state: ReduxState) => state.locale) + const { i18n } = initTranslations(locale) return ( - <> + @@ -125,7 +118,7 @@ export const Login: React.FC = (props) => { {branding.withMadeWithLove ? () : null} - + ) } diff --git a/src/frontend/components/property-type/array/add-new-item-translation.tsx b/src/frontend/components/property-type/array/add-new-item-translation.tsx index b2c967225..814bfbb60 100644 --- a/src/frontend/components/property-type/array/add-new-item-translation.tsx +++ b/src/frontend/components/property-type/array/add-new-item-translation.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Icon, Box } from '@adminjs/design-system' +import { Button, ButtonProps, Icon } from '@adminjs/design-system' import { useTranslation } from '../../../hooks' import { ResourceJSON, PropertyJSON } from '../../../interfaces' @@ -7,10 +7,10 @@ import { ResourceJSON, PropertyJSON } from '../../../interfaces' type AddNewItemButtonProps = { resource: ResourceJSON; property: PropertyJSON; -} +} & ButtonProps const AddNewItemButton: React.FC = (props) => { - const { resource, property } = props + const { resource, property, ...btnProps } = props const { translateProperty, translateButton } = useTranslation() const label = translateProperty( `${property.path}.addNewItem`, @@ -21,10 +21,10 @@ const AddNewItemButton: React.FC = (props) => { ) return ( - + ) } diff --git a/src/frontend/components/property-type/array/edit.spec.tsx b/src/frontend/components/property-type/array/edit.spec.tsx index d245269ff..222da34e1 100644 --- a/src/frontend/components/property-type/array/edit.spec.tsx +++ b/src/frontend/components/property-type/array/edit.spec.tsx @@ -76,10 +76,10 @@ describe('', function () { }) }) - xit('renders new empty input field after clicking "add"', function () { - const { getByText } = renderTestSubject(property, record) + it('renders new empty input field after clicking "add"', function () { + const { getByTestId } = renderTestSubject(property, record) - fireEvent.click(getByText(AddNewItemText)) + fireEvent.click(getByTestId(`${property.path}-add`)) expect(onChange).to.has.been.calledWith(property.path, ['']) }) diff --git a/src/frontend/components/property-type/array/edit.tsx b/src/frontend/components/property-type/array/edit.tsx index 80f9c5a29..11343eebf 100644 --- a/src/frontend/components/property-type/array/edit.tsx +++ b/src/frontend/components/property-type/array/edit.tsx @@ -38,26 +38,25 @@ const ItemRenderer: React.FC = (props) => { backgroundColor="white" flex flexDirection="row" - alignItems="center" + alignItems="start" data-testid={property.path} > - + - - - + )} @@ -121,9 +120,7 @@ const InputsInSection: React.FC = (props) => { ) })} {provided.placeholder} - + )} diff --git a/src/frontend/components/property-type/boolean/filter.tsx b/src/frontend/components/property-type/boolean/filter.tsx index 0dc5c4e76..f6f42baef 100644 --- a/src/frontend/components/property-type/boolean/filter.tsx +++ b/src/frontend/components/property-type/boolean/filter.tsx @@ -1,9 +1,10 @@ import React from 'react' -import { FormGroup, Label, Select } from '@adminjs/design-system' +import { FormGroup, Select } from '@adminjs/design-system' import mapValue from './map-value' import { FilterPropertyProps } from '../base-property-props' import allowOverride from '../../../hoc/allow-override' +import PropertyLabel from '../utils/property-label/property-label' const boolValue = (s: string): boolean => { if (/true/i.test(s)) { @@ -27,7 +28,7 @@ const Filter: React.FC = (props) => { return ( - +