| = Component Development |
| :toc: left |
| |
| == Overview |
| |
| The following list contains a possible approach for implementing a new UI component: |
| |
| 1. Create a new design or start with an existing design, see for example Figma |
| 2. Validate the use case and analyze the components that may be used for the implementation |
| 3. Create Composables that represent the UI component(s) and use placeholders for data population |
| 4. Create a component interface and implementation with the UI state and UI component interactions |
| 5. Create previews with preview component implementations to check the UI implementation |
| 6. Create a store and store provider for fetching resources and interacting with Solr backend |
| 7. Implement the client used by the store provider |
| 8. Write tests and test the new component |
| 9. If not already done, integrate the component in the existing application |
| 10. If not already done, extract resources like texts to allow internationalization and localization |
| |
| It is recommended to take a look at existing components, so that you get a better understanding |
| of how things are implemented, what they have in common, and how each technology is utilized. |
| |
| == Component's Logic |
| |
| === Components (Decompose) |
| |
| The component integration interacts with the UI composables and the state store. |
| |
| The implementation of the component interface "catches" user inputs like clicks and passes them |
| to the store as ``Intent``s. The intents are then handled by the store implementation and |
| may send a request to the backend and / or update the store state. The component is consuming |
| and mapping the store state to the UI state. So once the store state is updated, it will |
| reflect the changes in the UI. |
| |
| === State Stores and Store Providers |
| |
| The state stores manage the state of the application, but independent of the state that is |
| represented in the UI. Instances are created by store providers that hold the logic of the |
| store. |
| |
| Store providers consist of three elements: |
| |
| - an executor implementation that consumes actions and intents and creates messages and labels |
| - a reducer that updates the store state with the messages produced by the executor |
| - a function for retrieving an instance of the store |
| |
| The store provider does also define the interface for the client that has to be provided in |
| order for the executor to make API calls and interact with the Solr backend. |
| |
| == Component's Visuals |
| |
| === Composables |
| |
| Composables are the UI elements that are defined and styled. They can be seen as boxes, rows and |
| columns that are nested and change their style and structure based on conditions, state and input. |
| |
| There are many ways to get started, but the easiest way probably is to get familiar with the basics |
| and try things out. The Figma designs make use of almost the same elements for designing, |
| so the structure and configurations there may be mapped almost one-by-one in Compose code. |
| |
| === Styling |
| |
| The styling in Compose is done via ``Modifier``s. Each composable should normally accept a modifier |
| as a parameter, so that the user can customize specific visual parameters of the composable like |
| width, height and alignment in the parent's composable. |
| |
| Since we are using Material 3, you do not have to care much about colors, typography and shapes. |
| These are configured for the entire app, and you only have to make use of the right properties |
| that are provided by the theme. |
| |
| === Accessibility |
| |
| Compose comes with many accessibility features that can be used to improve the user experience. |
| |
| The simplest form of accessibility in a UI is probably the responsiveness of the UI. This is |
| realized with `WindowSizeClass`. Some composables may use a wrapper (usually suffixed with |
| `Content`) that checks the window size and loads different UI based on the dimensions of the |
| current window. |
| |
| Another accessibility feature is the resource loading based on the system's locale or the user's |
| preference. This allows the UI to be displayed in the user's native language. For that, you have |
| to simply provide translations in the Compose resources. |
| |
| Another accessibility feature often underestimated is coloring. Some people with color vision |
| deficiency may need a different theme, so that elements with problematic contrasts may be |
| better visible again. |
| |
| Additional accessibility features like font scaling, semantics for screen readers may also |
| be considered. Jetpack Compose provides a https://developer.android.com/develop/ui/compose/accessibility[simplified overview] |
| and https://developer.android.com/codelabs/jetpack-compose-accessibility#0[Codelabs] for getting started. |
| |
| === Navigation and Child Components |
| |
| Some components may have navigation elements and will load other components inside a frame layout. |
| Since components hold a hierarchical context that needs to be managed somehow, child components |
| (also used in navigation) are instantiated in a slightly different manner. |
| |
| Decompose provides https://arkivanov.github.io/Decompose/navigation/overview/[a few examples] |
| and details of the process behind the navigation and child components. |
| |
| == Additional Notes |
| |
| === Dependency Locking |
| |
| When adding or changing dependencies, you typically run `./gradlew resolveAndLockAll --write-locks`. |
| Since we are building a web application from kotlin sources, we also have to update the JS lockfile |
| with `./gradlew kotlinUpgradeYarnLock`. This will update the lockfile found at `kotlin-js-store/yarn.lock`. |
| |
| Some multiplatform libraries have platform-specific dependency resolution that will result in different |
| lockfiles being generated, based on the environment the lock task is executed. It is important to exclude |
| these platform-specific libraries from the lockfile to ensure a consistent lockfile generation across |
| different operating systems. |
| |
| Platform-specific libraries come with a module name suffix that includes the platform name, like |
| in `org.jetbrains.compose.desktop:desktop-jvm-windows-x64`. To identify those, look into the |
| changes after updating the lockfile and add the necessary ignore-clause if such libraries |
| exist. These ignore-clauses should be added in `gradle/validation/dependencies.gradle` inside the |
| `allprojects.dependencyLocking` block. |