Skip to content

Commit

Permalink
fix(app-platform): upgrade platform tools to use vite and react 18
Browse files Browse the repository at this point in the history
  • Loading branch information
kabaros committed Oct 22, 2024
1 parent 1d962c5 commit 99d9f2a
Show file tree
Hide file tree
Showing 139 changed files with 7,458 additions and 61 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
![React 18](https://img.shields.io/badge/react-18-blue)
# Scheduler

## Cypress env settings
Expand Down
122 changes: 61 additions & 61 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,63 +1,63 @@
{
"name": "scheduler-app",
"private": true,
"scripts": {
"build": "d2-app-scripts build",
"start": "d2-app-scripts start",
"start:nobrowser": "BROWSER=none d2-app-scripts start",
"test": "d2-app-scripts test --coverage",
"test:watch": "d2-app-scripts test --watch",
"lint": "d2-style check",
"format": "d2-style apply",
"cypress": "start-server-and-test 'yarn start:nobrowser' 3000 'yarn exec cypress open'"
},
"dependencies": {
"@dhis2/app-runtime": "^3.8.0",
"@dhis2/d2-i18n": "^1.1.0",
"@dhis2/prop-types": "2.0.3",
"@dhis2/ui": "^9.11.8",
"@testing-library/react": "^16.0.1",
"classnames": "^2.3.1",
"cronstrue": "^1.114.0",
"history": "^4.9.0",
"moment": "^2.29.1",
"package.json": "^2.0.1",
"prop-types": "^15.8.1",
"react-router": "^5.0.1",
"react-router-dom": "^5.2.0",
"styled-jsx": "^4.0.1"
},
"devDependencies": {
"@badeball/cypress-cucumber-preprocessor": "^20.0.3",
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
"@cypress/webpack-preprocessor": "^6.0.1",
"@dhis2/cli-app-scripts": "^12.0.0-alpha.19",
"@dhis2/cli-style": "^10.7.4",
"@testing-library/cypress": "^10.0.1",
"cypress": "^13.7.2",
"enzyme": "^3.10.0",
"eslint-plugin-compat": "^3.9.0",
"eslint-plugin-i18next": "^5.1.1",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsx-a11y": "^6.4.1",
"identity-obj-proxy": "^3.0.0",
"start-server-and-test": "^2.0.3"
},
"jest": {
"setupFilesAfterEnv": [
"<rootDir>/src/setupTests.js"
],
"collectCoverageFrom": [
"src/**/*.{js,jsx}",
"!src/{index.js,serviceWorker.js,setupTests.js}"
],
"coveragePathIgnorePatterns": [
"/node_modules/",
"/src/locales/"
],
"moduleNameMapper": {
"\\.css$": "identity-obj-proxy"
}
},
"version": "101.6.12"
"name": "scheduler-app",
"private": true,
"scripts": {
"build": "d2-app-scripts build",
"start": "d2-app-scripts start",
"start:nobrowser": "BROWSER=none d2-app-scripts start",
"test": "d2-app-scripts test --coverage",
"test:watch": "d2-app-scripts test --watch",
"lint": "d2-style check",
"format": "d2-style apply",
"cypress": "start-server-and-test 'yarn start:nobrowser' 3000 'yarn exec cypress open'"
},
"dependencies": {
"@dhis2/app-runtime": "^3.8.0",
"@dhis2/d2-i18n": "^1.1.0",
"@dhis2/prop-types": "2.0.3",
"@dhis2/ui": "^9.11.8",
"@testing-library/react": "^16.0.1",
"classnames": "^2.3.1",
"cronstrue": "^1.114.0",
"history": "^4.9.0",
"moment": "^2.29.1",
"package.json": "^2.0.1",
"prop-types": "^15.8.1",
"react-router": "^5.0.1",
"react-router-dom": "^5.2.0",
"styled-jsx": "^4.0.1"
},
"devDependencies": {
"@badeball/cypress-cucumber-preprocessor": "^20.0.3",
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
"@cypress/webpack-preprocessor": "^6.0.1",
"@dhis2/cli-app-scripts": "^12.0.0-alpha.19",
"@dhis2/cli-style": "^10.7.4",
"@testing-library/cypress": "^10.0.1",
"cypress": "^13.7.2",
"enzyme": "^3.10.0",
"eslint-plugin-compat": "^3.9.0",
"eslint-plugin-i18next": "^5.1.1",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsx-a11y": "^6.4.1",
"identity-obj-proxy": "^3.0.0",
"start-server-and-test": "^2.0.3"
},
"jest": {
"setupFilesAfterEnv": [
"<rootDir>/src/setupTests.js"
],
"collectCoverageFrom": [
"src/**/*.{js,jsx}",
"!src/{index.js,serviceWorker.js,setupTests.js}"
],
"coveragePathIgnorePatterns": [
"/node_modules/",
"/src/locales/"
],
"moduleNameMapper": {
"\\.css$": "identity-obj-proxy"
}
},
"version": "101.6.12"
}
25 changes: 25 additions & 0 deletions src/components/App/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react'
import { CssVariables } from '@dhis2/ui'
import { Routes } from '../Routes'
import { AuthWall } from '../AuthWall'
import { Store } from '../Store'
import { PageWrapper } from '../PageWrapper'
import './App.css'

/* eslint-disable-next-line import/no-unassigned-import -- Necessary for translations to work */
import '../../locales'

const App = () => (
<React.Fragment>
<CssVariables spacers colors theme />
<PageWrapper>
<AuthWall>
<Store>
<Routes />
</Store>
</AuthWall>
</PageWrapper>
</React.Fragment>
)

export default App
9 changes: 9 additions & 0 deletions src/components/App/App.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react'

Check failure on line 1 in src/components/App/App.test.jsx

View workflow job for this annotation

GitHub Actions / lint / lint

No exports found
import { shallow } from 'enzyme'
import App from './App.jsx'

describe('<App>', () => {
it('renders without errors', () => {
shallow(<App />)
})
})
58 changes: 58 additions & 0 deletions src/components/AuthWall/AuthWall.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react'
import PropTypes from 'prop-types'
import { NoticeBox } from '@dhis2/ui'
import i18n from '@dhis2/d2-i18n'
import { useDataQuery } from '@dhis2/app-runtime'
import { Spinner } from '../Spinner'
import { getAuthorized } from './selectors'
import styles from './AuthWall.module.css'

const query = {
me: {
resource: 'me',
},
}

const AuthWall = ({ children }) => {
const { loading, error, data } = useDataQuery(query)

if (loading) {
return <Spinner />
}

if (error) {
return (
<div className={styles.noticeBoxWrapper}>
<NoticeBox error title={i18n.t('Something went wrong')}>
{i18n.t(
'Something went wrong whilst retrieving user permissions.'
)}
</NoticeBox>
</div>
)
}

const isAuthorized = getAuthorized(data.me)

if (!isAuthorized) {
return (
<div className={styles.noticeBoxWrapper}>
<NoticeBox error title={i18n.t('Not authorized')}>
{i18n.t(
"You don't have access to the Job Scheduler. Contact a system administrator to request access."
)}
</NoticeBox>
</div>
)
}

return <React.Fragment>{children}</React.Fragment>
}

const { node } = PropTypes

AuthWall.propTypes = {
children: node.isRequired,
}

export default AuthWall
72 changes: 72 additions & 0 deletions src/components/AuthWall/AuthWall.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react'

Check failure on line 1 in src/components/AuthWall/AuthWall.test.jsx

View workflow job for this annotation

GitHub Actions / lint / lint

No exports found
import { shallow, mount } from 'enzyme'
import { useDataQuery } from '@dhis2/app-runtime'
import { getAuthorized } from './selectors'
import AuthWall from './AuthWall.jsx'

jest.mock('@dhis2/app-runtime', () => ({
useDataQuery: jest.fn(),
}))

jest.mock('./selectors', () => ({
getAuthorized: jest.fn(),
}))

afterEach(() => {
jest.resetAllMocks()
})

describe('<AuthWall>', () => {
it('shows a spinner when loading', () => {
useDataQuery.mockImplementation(() => ({ loading: true }))

const wrapper = mount(<AuthWall>Child</AuthWall>)

Check failure on line 23 in src/components/AuthWall/AuthWall.test.jsx

View workflow job for this annotation

GitHub Actions / lint / lint

disallow literal string
const loadingIndicator = wrapper.find({
'data-test': 'dhis2-uicore-circularloader',
})

expect(loadingIndicator).toHaveLength(1)
})

it('shows a noticebox for fetching errors', () => {
const message = 'Something went wrong'
const error = new Error(message)

useDataQuery.mockImplementation(() => ({
loading: false,
error,
}))

const wrapper = shallow(<AuthWall>Child</AuthWall>)

Check failure on line 40 in src/components/AuthWall/AuthWall.test.jsx

View workflow job for this annotation

GitHub Actions / lint / lint

disallow literal string
const noticebox = wrapper.find('NoticeBox')

expect(noticebox).toHaveLength(1)
})

it('shows a noticebox for unauthorized users', () => {
useDataQuery.mockImplementation(() => ({
loading: false,
error: undefined,
data: {},
}))
getAuthorized.mockImplementation(() => false)

const wrapper = shallow(<AuthWall>Child</AuthWall>)

Check failure on line 54 in src/components/AuthWall/AuthWall.test.jsx

View workflow job for this annotation

GitHub Actions / lint / lint

disallow literal string
const noticebox = wrapper.find('NoticeBox')

expect(noticebox).toHaveLength(1)
})

it('renders the children for users that are authorized', () => {
useDataQuery.mockImplementation(() => ({
loading: false,
error: undefined,
data: {},
}))
getAuthorized.mockImplementation(() => true)

const wrapper = shallow(<AuthWall>Child</AuthWall>)

Check failure on line 68 in src/components/AuthWall/AuthWall.test.jsx

View workflow job for this annotation

GitHub Actions / lint / lint

disallow literal string

expect(wrapper.text()).toEqual(expect.stringContaining('Child'))
})
})
39 changes: 39 additions & 0 deletions src/components/Buttons/CronPresetButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { Button } from '@dhis2/ui'
import i18n from '@dhis2/d2-i18n'
import { CronPresetModal } from '../Modal'

const CronPresetButton = ({ setCron, small }) => {
const [showModal, setShowModal] = useState(false)

return (
<React.Fragment>
<Button onClick={() => setShowModal(true)} small={small}>
{i18n.t('Choose from preset times')}
</Button>
{showModal && (
<CronPresetModal
hideModal={
/* istanbul ignore next */
() => setShowModal(false)
}
setCron={setCron}
/>
)}
</React.Fragment>
)
}

CronPresetButton.defaultProps = {
small: false,
}

const { func, bool } = PropTypes

CronPresetButton.propTypes = {
setCron: func.isRequired,
small: bool,
}

export default CronPresetButton
23 changes: 23 additions & 0 deletions src/components/Buttons/CronPresetButton.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react'

Check failure on line 1 in src/components/Buttons/CronPresetButton.test.jsx

View workflow job for this annotation

GitHub Actions / lint / lint

No exports found
import { shallow, mount } from 'enzyme'
import CronPresetButton from './CronPresetButton.jsx'

describe('<CronPresetButton>', () => {
it('renders without errors', () => {
shallow(<CronPresetButton setCron={() => {}} />)
})

it('renders without errors when small', () => {
shallow(<CronPresetButton setCron={() => {}} small />)
})

it('shows the modal when button is clicked', () => {
const wrapper = mount(<CronPresetButton setCron={() => {}} />)

expect(wrapper.find('CronPresetModal')).toHaveLength(0)

wrapper.find('button').simulate('click')

expect(wrapper.find('CronPresetModal')).toHaveLength(1)
})
})
36 changes: 36 additions & 0 deletions src/components/Buttons/DeleteJobButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { Button } from '@dhis2/ui'
import i18n from '@dhis2/d2-i18n'
import { DeleteJobModal } from '../Modal'

const DeleteJobButton = ({ id, onSuccess }) => {
const [showModal, setShowModal] = useState(false)

return (
<React.Fragment>
<Button destructive onClick={() => setShowModal(true)}>
{i18n.t('Delete job')}
</Button>
{showModal && (
<DeleteJobModal
id={id}
hideModal={
/* istanbul ignore next */
() => setShowModal(false)
}
onSuccess={onSuccess}
/>
)}
</React.Fragment>
)
}

const { string, func } = PropTypes

DeleteJobButton.propTypes = {
id: string.isRequired,
onSuccess: func.isRequired,
}

export default DeleteJobButton
Loading

0 comments on commit 99d9f2a

Please sign in to comment.