Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move to Vue 3 #526

Closed
wants to merge 58 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
7814c4c
Replace propsData with props for Vue Test Utils
matthew-white Jan 18, 2022
fcc27b0
Make changes now that Vue Test Utils findAll() returns different type
matthew-white Jan 18, 2022
d1ef21f
Update link to Vue Test Utils
matthew-white Jan 18, 2022
c1d7e8e
Remove Vue.config.productionTip
matthew-white Jan 18, 2022
29ac549
Remove $set()
matthew-white Jan 18, 2022
fffb9c8
Use markRaw() for select data properties
matthew-white Jan 18, 2022
d1d2852
Replace $listeners with $attrs
matthew-white Jan 18, 2022
5aff7e3
Replace "destroy" with "unmount"
matthew-white Jan 18, 2022
5e8a4c0
Make changes related to v-model
matthew-white Jan 18, 2022
330c708
Add emits option to components
matthew-white Jan 18, 2022
7f8b98c
Update how app is initialized
matthew-white Jan 18, 2022
e090a33
Update how router is initialized
matthew-white Jan 18, 2022
afb4ce3
Account for removal of standalone * from route path pattern
matthew-white Jan 18, 2022
427f100
Simplify navigation guards
matthew-white Jan 18, 2022
48f8be8
Simplify access to route meta fields
matthew-white Jan 20, 2022
4ddc6c4
Account for changed return value of router.resolve()
matthew-white Jan 20, 2022
301156a
Account for changed type of router.currentRoute
matthew-white Jan 20, 2022
2a8299f
Remove currentRoute from store
matthew-white Jan 20, 2022
98de278
Remove anyNavigationConfirmed from store
matthew-white Jan 20, 2022
ecf8fe5
Use markRaw() in SubmissionFieldDropdown
matthew-white Jan 20, 2022
c1d3381
Temporarily remove password strength meter
matthew-white Jan 20, 2022
1f33273
Update how Vue I18n is initialized
matthew-white Jan 20, 2022
4f16351
Account for changes to translation component
matthew-white Jan 20, 2022
8713a6f
Do more to account for i18n special characters
matthew-white Jan 20, 2022
32711ab
Remove unneeded method from ComponentInterpolationNode
matthew-white Jan 20, 2022
5a55d00
Make small changes to Translations.prototype.toJSON()
matthew-white Jan 20, 2022
acba324
Update Vue CLI options for Vue I18n
matthew-white Jan 20, 2022
25b3ff6
Add factory for router
matthew-white Jan 20, 2022
1f2b8fb
Move alert out of store
matthew-white Jan 20, 2022
a9a9c84
Move config out of store
matthew-white Jan 20, 2022
bb0fe73
Move sendInitialRequests out of store
matthew-white Jan 20, 2022
2ff7773
Move unsavedChanges out of store
matthew-white Jan 20, 2022
8c1cd2e
Remove unneeded presenters
matthew-white Jan 20, 2022
8159dd1
Move files related to request module
matthew-white Jan 20, 2022
d661f89
Move request data out of store
matthew-white Jan 20, 2022
97db863
Refactor UserList and ProjectUserList
matthew-white Jan 20, 2022
9cbda91
Add factory for i18n
matthew-white Jan 20, 2022
a796f63
Add missing file header
matthew-white Jan 20, 2022
edb678b
Move $tcn() to global mixin
matthew-white Jan 20, 2022
f0314f4
Rename parameter of $tcn()
matthew-white Jan 20, 2022
6c3b98b
Consolidate Mocha hooks
matthew-white Jan 20, 2022
faf86eb
Move logger into container
matthew-white Jan 20, 2022
86ef123
Use consistent methods to log errors
matthew-white Jan 20, 2022
d23423f
Move http into container
matthew-white Jan 20, 2022
bd22b20
Update unit tests related to requests
matthew-white Jan 20, 2022
e2be7a6
Split up setup.js, moving style imports into separate file
matthew-white Jan 20, 2022
7b50764
Move select mixins to src/reusables
matthew-white Jan 20, 2022
151523b
Add object to manage modal data
matthew-white Jan 20, 2022
9d2bd5c
Add useCallWait() composable in addition to mixin
matthew-white Jan 20, 2022
90bc354
Rename routes mixin and add composable
matthew-white Jan 20, 2022
85f79cc
Add useRequests() composable in addition to mixin
matthew-white Jan 20, 2022
21a62b0
Add useTabs() composable in addition to mixin
matthew-white Jan 20, 2022
7b1f446
Rename remaining mixins
matthew-white Jan 20, 2022
591a7e2
Use Composition API in App component
matthew-white Jan 20, 2022
22dbc72
Use Composition API for components that use reconcileData
matthew-white Jan 20, 2022
9a4e2c3
Use provide/inject with SubmissionList
matthew-white Jan 20, 2022
e6c29b4
Update linter for Vue 3
matthew-white Jan 20, 2022
ab4f113
Update .gitignore to match latest version of Vue CLI
matthew-white Jan 20, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module.exports = {
ecmaVersion: 2020
},
extends: [
'plugin:vue/recommended',
'plugin:vue/vue3-recommended',
'@vue/airbnb'
],
env: {
Expand Down Expand Up @@ -41,6 +41,7 @@ module.exports = {
minProperties: 0,
consistent: true
}],
'object-property-newline': 'off',
'operator-linebreak': ['error', 'after', {
overrides: { '?': 'before', ':': 'before' }
}],
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Editor directories and files
.idea
Expand Down
16 changes: 9 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,11 @@ Most components are named according to the combination of a resource and an acti
* `Edit`. A component used to update an existing resource of a particular type.
* `Delete`. A modal used to delete an existing resource of a particular type.

### Vue Mixins
### Reusability: Mixins and the Composition API

Each component may use one or more mixins. Each file in [`/src/mixins/`](/src/mixins/) exports a mixin factory for a single type of mixin. (We use factories so that the component can pass in options for the mixin. We don't use this pattern much anymore though, so we will likely change this when we move to Vue 3.)
Components can share reusable code. They can do so using either mixins or the Composition API. Several pieces of reusable code are available as both a mixin and a composable: see [`/src/reusables/`](/src/reusables/). A component using the Options API should use the mixin, while a component using the Composition API should use the composable. Some pieces of reusable code are currently only available as a mixin: see [`/src/mixins/`](/src/mixins/).

ODK Central Frontend recently moved from Vue 2 and Vue 3, and we are still establishing patterns in this area. Currently, the vast majority of components use the Options API, but that may change: we could start preferring the Composition API for new components. Alternatively, we may decide to leave the Composition API for specialized cases. How we end up using the Composition API will influence how we approach mixins and composables in the future.

### Router

Expand All @@ -127,9 +129,9 @@ The initial navigation is asynchronous, in part because it tries to restore the

If the user navigates to a location to which the `validateData` meta field forbids access, the user will be redirected to either `/` (if they are logged in) or `/login` (if they are not logged in). That means that unless the user is already at `/` or `/login`, navigating to a different location will always result in some confirmed navigation: either the first navigation will be confirmed, or the redirect will be confirmed. In other words, the user is guaranteed to navigate elsewhere: the route will change. Nonetheless, `router.push()` and `router.replace()` may return a rejected promise, if the first navigation is aborted. If that is a possibility, call `catch()` on the promise.

Before the user's session expires, the user will be automatically redirected to `/login`. Further, in general, the user is permitted to change the route at any time. That means that components should be prepared to be destroyed at any point. For example, if a component starts asynchronous work, it should probably check when that work completes whether the route has changed.
Before the user's session expires, the user will be automatically redirected to `/login`. Further, in general, the user is permitted to change the route at any time. That means that components should be prepared to be unmounted at any point. For example, if a component starts asynchronous work, it should probably check when that work completes whether the route has changed.

We store router state in the Vuex store (see [`/src/store/modules/router.js`](/src/store/modules/router.js)). Some router-related utilities are defined in [`/src/util/router.js`](/src/util/router.js), and components can access router-related methods by using the `routes` mixin ([`/src/mixins/routes.js`](/src/mixins/routes.js)).
We store router state in the Vuex store (see [`/src/store/modules/router.js`](/src/store/modules/router.js)). Some router-related utilities are defined in [`/src/util/router.js`](/src/util/router.js), and components can access router-related methods by using `usePaths()` or `mixinPaths` ([`/src/reusables/paths.js`](/src/reusables/paths.js)).

The router is responsible for updating the document title (text in the browser tab and history) when navigating to a new route. It uses the `parts()` function in a route record's `meta.title` field. Much like with `validateData`, it may not have all the information it needs when first changing the route (e.g. navigating to a new Project page), so it watches for changes to the Vuex store (the key to watch is defined in `meta.title.key`) and then updates the page title again (e.g. using a Project's name after the project info has been fetched from the backend.)

Expand Down Expand Up @@ -231,7 +233,7 @@ To add a new locale to ODK Central Frontend:
1. Add the locale to Transifex.
2. Add the locale to `locales` in [`/src/i18n.js`](/src/i18n.js) and [`/bin/util/transifex.js`](/bin/util/transifex.js).
3. If the locale pluralizes differently from the default, specify its pluralization rules in `/src/i18n.js`.
4. Check that there is a flatpickr config for the locale. If there isn't one, create a GitHub issue in this repository or contact us on Slack.
4. Import the flatpickr config for the locale in [`DateRangePicker`](/src/components/date-range-picker.vue).
5. Consider spot-checking the translations. In particular, check that messages used in component interpolation have been translated correctly.

Note that right now, the router will use the user's preferred language to load the locale, but it will only use the first subtag of the language. If/when we add a locale with multiple subtags, we will need to update the router.
Expand Down Expand Up @@ -306,7 +308,7 @@ You can use `mockHttp().testStandardButton()` to test some of these things for a

To run tests, type `npm run test`. This will run all `*.spec.js` files in [`/test/`](/test/).

The core of our tests is the tests of the components and the tests of the router, which together implement most of the business logic. We also have unit tests of mixins, the Vuex store, utility functions, and so on. The directory structure of `/test/` largely mirrors that of `/src/`. For example, each test file in `/test/components/` corresponds to a component in `/src/components/`; each test file in `/test/mixins/` corresponds to a mixin in `/src/mixins/`. The exception is that the files in `/test/util/` do not test the files in `/src/util/`, but rather are test utility functions. The files in `/src/util/` are tested by those in `/test/unit/`.
The core of our tests is the tests of the components and the tests of the router, which together implement most of the business logic. We also have unit tests of composables/mixins, the Vuex store, utility functions, and so on. The directory structure of `/test/` largely mirrors that of `/src/`. For example, each test file in `/test/components/` corresponds to a component in `/src/components/`; each test file in `/test/reusables/` corresponds to a file in `/src/reusables/`. The exception is that the files in `/test/util/` do not test the files in `/src/util/`, but rather are test utility functions. The files in `/src/util/` are tested by those in `/test/unit/`.

If you add code outside a `.vue` file (for example, a utility function), and it is easy to test in isolation, consider writing unit tests to verify the code. If a function's input is very easy to directly construct or mock, for example, it is likely a good candidate for unit testing. On the other hand, when writing code that is more closely related to other code, it may work best to test the functionality in a component test or a test of the router.

Expand All @@ -323,7 +325,7 @@ Our tests use a number of external packages:

We extend Should.js assertions in [`/test/assertions.js`](/test/assertions.js).

[Vue Test Utils](https://vue-test-utils.vuejs.org/) renders Vue components for testing, allowing you to test that a component renders and behaves as expected. We have built some functionality on top of Vue Test Utils, in particular [`mount()`](/test/util/lifecycle.js). We define components used only for testing in [`/test/util/components/`](/test/util/components/).
[Vue Test Utils](https://next.vue-test-utils.vuejs.org/) renders Vue components for testing, allowing you to test that a component renders and behaves as expected. We have built some functionality on top of Vue Test Utils, in particular [`mount()`](/test/util/lifecycle.js). We define components used only for testing in [`/test/util/components/`](/test/util/components/).

Many tests involve sending a request. You can mock a series of request-response cycles by using `load()` or `mockHttp()`, defined in [`/test/util/http.js`](/test/util/http.js). You can use these to implement common tests, for example, testing some standard button things: see [`/test/util/http/common.js`](/test/util/http/common.js).

Expand Down
Loading