mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Refactor and transform "Application update", "Preferences" and "Application menu" into Features (#6437)
* Move some code for application update to feature directory The rest of the code could not be moved yet because of work-in-progress refactorings for OCP compliance. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce helper to get a global override for a function-injectable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract global override-files from bloated getDiForUnitTesting Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Move some more code for application update to feature directory Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce competition for top bar items to achieve OCP-compliance Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for opening context menu using the new competition Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for navigating to home as OCP Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for navigating to back as OCP Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for navigating to forward as OCP Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for application update as OCP Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Format code to make ongoing refactoring a bit easier Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Add missing unit tests for top bar extendability using extension API This makes ongoing refactoring easier. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Replace implementation for old top-bar items for losing competition Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for window controls as OCP Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce reusable component for spacing between other components Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce helper component to render list of React elements Features: - Placeholder for empty list - Separators between items - No boilerplate for "key" prop in React Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix double-clicking and dragging of window from top bar Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update snapshots Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix stuff broken in rebase Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Update snapshots after rebase Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make fake time have a default value for "now" Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Migrate some application menu items to injectables Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate separators of application menu in single file Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove duplication from separators in application menu Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract some operation system actions from application menu as injectables Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract menu item for quitting application as injectable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce way to type narrow a string property Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make the di's for unit testing able to auto-register also named exports Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make global override less strict to simplify setup of many unit tests There's a better solution for this in the horizon, as this overridden thing is better faked than stubbed. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify a test Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce way to create hierarchical composites from a flat array Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Implement hierarchy of application menu items using "many-root" composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Migrate more application menu items to injectables Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Simplify hierarchy of application menu items using "single-root" composite Also solve composed typing of application menu by using Discriminated Unions of TypeScript, see: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update snapshot Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify creation of composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make composite able to have custom handler for missing parent ids This will be useful next for application menu items, where a missing parent id cannot be fatal. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Defend against self-referencing composites Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Reintroduce non-fatal handling of orphan application menu items Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make faked, yet weak, typing a bit stronger Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify getting of composite paths Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make registrator for application menu items support all known scenarios Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Add logging for unrecognizable application menu item types Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Rename id of menu item to keep it discoverable by existing extensions Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate code to check for updates closer to feature Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce reusable horizontal line Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for preferences as a Feature Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for terminal preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for editor preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for proxy preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for telemetry preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for application preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move code related to helm chart preferences under related Feature in preparation for competition Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for kubernetes preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for application preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for editor preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for kubernetes preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for proxy preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add missing observer to make sure component updates Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for telemetry preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for terminal preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak UI for preferences Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove dead code Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak more UI of preferences Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Revert "Introduce reusable horizontal line" This reverts commit 4d8c147fe0f1a14bd884f73cf345e7d3a28b954a. Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove dead code Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce utility to find exactly one item from array Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for preferences navigation Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move code under a Feature Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove usage of old code Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove dead code Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add extensions tab group to preferences Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce way to find out if composite has a descendant This will serve eg. hiding of empty preference tab groups. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make tab groups and tabs in preferences not render when there is no content Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove code made redundant with hiding of preference tabs without content Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Tweak UI for preference navigation tab groups Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce test helper to abstract discovery of HTML elements Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Kill dead code Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate discovery of HTML elements in some tests Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update snapshots Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt application builder to changes in preference navigation Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt test setup to changes in preference navigation Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove uninteresting technical tests that are covered by behavioural ones Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate discovery of HTML elements in a test Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove test ID made redundant by consolidating discovery of HTML elements Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove duplication from preference pages Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix import Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make queries in element discovery return matching attribute values for easier testing Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make element discovery able to do nested discovery Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make element discovery able to discover without value for attribute Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Implement registrator for preference items Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update snapshots Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make styling less brittle by not relying on static HTML-element structures with CSS-rules Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Add todo Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove "group" from preference types, as it is exact replica of "item" Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Update snapshots Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Replace usages of react-component factory with actual components for simplicity Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Start considering application preferences as default tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate naming Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make sense in horizontal lines Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Clean public interface of a normalize composite Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate name of function Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate directory structure of composite Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move utility functions to common Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Rename a preference item type to better communicate intent in UI Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move shared UI component to more common place Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate sizing of horizontal line to t-shirts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move application update related preferences under application update feature Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make winner of competition to use original route for preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make HTML element discovery require less parameters Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract "composable-responsibilities" for Discriminable, Labelable, Orderable, and Showable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Update snapshot Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove dead code Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move code under a feature Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate navigating to preferences Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move code under a feature Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Relocate code under a sub-feature Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add TODO Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove dead code Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix merge conflicts Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update snapshots after rebase Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix import path after rebase Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove duplication from exhaustiveness checks for discriminating unions Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add TODO Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update link to a more recent article Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify static Showability of a PreferenceItem Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Move general function to general directory Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract responsibility of "separability" Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make separator in Map-component know left and right item Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove additional separators when separated items are not shown for having no content Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Update snapshots Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix lint error Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt integration test to recent changes Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt more integration tests to recent changes Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make composite not care about in formatting of ids Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Adapt application builder and tests to array-like paths over string-like paths Array-like paths do not have weakness for special characters as part of id, such as ".". Also note: the error messaging for clicking of application menu in application builder is a bit worse now I think, but the simplification of the test code is worth it in this case IMHO. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make composite not care about formatting of ids Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Adapt application builder and tests to array-like paths over string-like paths Array-like paths do not have weakness for special characters as part of id, such as ".". Also note: the error messaging for clicking of application menu in application builder is a bit worse now I think, but the simplification of the test code is worth it in this case IMHO. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate output of get-composite-path to match find-composite's input Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make global overrides for functions log args of the call for devability Also make the thrown error suggest how to fix the problem. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make attempts to log error throw in unit tests Errors cannot be allowed to happen without a unit test explicitly causing it. Errors cannot be allowed to happen without author of unit test knowing it. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make composite not know about how children are transformed Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove some duplication from tests of composite Also make the thrown error suggest how to fix the problem. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make composite unit test an unrealistic test scenario about undefined ids Also make the thrown error suggest how to fix the problem. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Kill dead code Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify unit tests for composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate tests to now point-free composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify "Showable" Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make non-shown application menu items not break composite structure This was made apparent by adding related unit tests for all known environments. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify usages of Orderable and Showable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract being maybe Showable as explicit composable responsibility Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make more showables maybe showable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate logic for application menu for Windows to be based on composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Start using named export for composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Rename type for accuracy Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove unnecessary type and value Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make composable responsibilities readonly to nudge towards immutability Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove a bit of duplication to create TS-constants Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make a comment and test name make more sense Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify fallthrough in a switch/case Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Replace inline styles with proper CSS Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Switch to correct type to indicate "object which might not contain a property" Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify overriding of platform in a unit test Also make typing of platforms more strict, and remove some magic strings. Also add a TODO for further OCP-ification. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate some "maybe-types" and arguments using them Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Update snapshots Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
1 parent
38feab813c
commit
37eb236948
418 files changed
+39647
-27024
No files matched your search
-39
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import { rootFrameChildComponentInjectionToken } from "../../frames/root-frame/root-frame-child-component-injection-token";
|
||||
import { ForceUpdateModal } from "./force-update-modal";
|
||||
import timeSinceUpdateWasDownloadedInjectable from "./time-since-update-was-downloaded.injectable";
|
||||
import updateDownloadedDateTimeInjectable from "../../../common/application-update/update-downloaded-date-time/update-downloaded-date-time.injectable";
|
||||
import timeAfterUpdateMustBeInstalledInjectable from "./time-after-update-must-be-installed.injectable";
|
||||
|
||||
const forceUpdateModalRootFrameComponentInjectable = getInjectable({
|
||||
id: "force-update-modal-root-frame-component",
|
||||
|
||||
instantiate: (di) => {
|
||||
const timeSinceUpdateWasDownloaded = di.inject(timeSinceUpdateWasDownloadedInjectable);
|
||||
const updateDownloadedDateTime = di.inject(updateDownloadedDateTimeInjectable);
|
||||
const timeWhenUpdateMustBeInstalled = di.inject(timeAfterUpdateMustBeInstalledInjectable);
|
||||
|
||||
return {
|
||||
id: "force-update-modal",
|
||||
Component: ForceUpdateModal,
|
||||
|
||||
shouldRender: computed(
|
||||
() =>
|
||||
!!updateDownloadedDateTime.value.get() &&
|
||||
timeSinceUpdateWasDownloaded.get() >= timeWhenUpdateMustBeInstalled,
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: rootFrameChildComponentInjectionToken,
|
||||
|
||||
// Note: Globally overridden because it observes the current time which can cause long running tests
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default forceUpdateModalRootFrameComponentInjectable;
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
$baseline: 8px;
|
||||
|
||||
.ForceUpdateModal {
|
||||
background-color: white;
|
||||
border-radius: 1 * $baseline;
|
||||
padding: 3 * $baseline;
|
||||
width: 50 * $baseline;
|
||||
|
||||
.header {
|
||||
margin-bottom: 2 * $baseline;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-bottom: 2 * $baseline;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import React from "react";
|
||||
import restartAndInstallUpdateInjectable from "../../components/update-button/restart-and-install-update.injectable";
|
||||
import { Countdown } from "../../components/countdown/countdown";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import installUpdateCountdownInjectable from "./install-update-countdown.injectable";
|
||||
import { Dialog } from "../../components/dialog";
|
||||
import { Button } from "../../components/button";
|
||||
import styles from "./force-update-modal.module.scss";
|
||||
|
||||
interface Dependencies {
|
||||
restartAndInstallUpdate: () => void;
|
||||
secondsTill: IComputedValue<number>;
|
||||
}
|
||||
|
||||
const NonInjectedForceUpdateModal = observer(
|
||||
({ restartAndInstallUpdate, secondsTill }: Dependencies) => (
|
||||
<Dialog isOpen={true} pinned>
|
||||
<div
|
||||
data-testid="must-update-immediately"
|
||||
className={styles.ForceUpdateModal}
|
||||
>
|
||||
<div className={styles.header}>
|
||||
<h2>Please update</h2>
|
||||
</div>
|
||||
|
||||
<div className={styles.content}>
|
||||
<p>
|
||||
An update to Lens Desktop is required to continue using the application.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.footer}>
|
||||
<Button
|
||||
primary
|
||||
data-testid="update-now-from-must-update-immediately-modal"
|
||||
onClick={restartAndInstallUpdate}
|
||||
label="Update"
|
||||
>
|
||||
{" "}
|
||||
(
|
||||
<Countdown
|
||||
secondsTill={secondsTill}
|
||||
data-testid="countdown-to-automatic-update"
|
||||
/>
|
||||
)
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
),
|
||||
);
|
||||
|
||||
export const ForceUpdateModal = withInjectables<Dependencies>(
|
||||
NonInjectedForceUpdateModal,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
restartAndInstallUpdate: di.inject(restartAndInstallUpdateInjectable),
|
||||
secondsTill: di.inject(installUpdateCountdownInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import countdownStateInjectable from "../../components/countdown/countdown-state.injectable";
|
||||
import secondsAfterInstallStartsInjectable from "./seconds-after-install-starts.injectable";
|
||||
import restartAndInstallUpdateInjectable from "../../components/update-button/restart-and-install-update.injectable";
|
||||
|
||||
const installUpdateCountdownInjectable = getInjectable({
|
||||
id: "install-update-countdown",
|
||||
|
||||
instantiate: (di) => {
|
||||
const secondsAfterInstallStarts = di.inject(secondsAfterInstallStartsInjectable);
|
||||
const restartAndInstallUpdate = di.inject(restartAndInstallUpdateInjectable);
|
||||
|
||||
return di.inject(countdownStateInjectable, {
|
||||
startFrom: secondsAfterInstallStarts,
|
||||
|
||||
onZero: () => {
|
||||
restartAndInstallUpdate();
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default installUpdateCountdownInjectable;
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
const secondsAfterInstallStartsInjectable = getInjectable({
|
||||
id: "seconds-after-install-starts",
|
||||
instantiate: () => 90,
|
||||
});
|
||||
|
||||
export default secondsAfterInstallStartsInjectable;
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
|
||||
|
||||
const timeAfterUpdateMustBeInstalledInjectable = getInjectable({
|
||||
id: "time-after-update-must-be-installed",
|
||||
instantiate: () => THIRTY_DAYS,
|
||||
});
|
||||
|
||||
export default timeAfterUpdateMustBeInstalledInjectable;
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import assert from "assert";
|
||||
import { computed } from "mobx";
|
||||
import moment from "moment";
|
||||
import updateDownloadedDateTimeInjectable from "../../../common/application-update/update-downloaded-date-time/update-downloaded-date-time.injectable";
|
||||
import { reactiveNow } from "../../../common/utils/reactive-now/reactive-now";
|
||||
|
||||
const timeSinceUpdateWasDownloadedInjectable = getInjectable({
|
||||
id: "time-since-update-was-downloaded",
|
||||
|
||||
instantiate: (di) => {
|
||||
const updateDownloadedDateTime = di.inject(updateDownloadedDateTimeInjectable);
|
||||
|
||||
return computed(() => {
|
||||
const currentTimestamp = reactiveNow();
|
||||
|
||||
const downloadedAt = updateDownloadedDateTime.value.get();
|
||||
|
||||
assert(downloadedAt);
|
||||
|
||||
return currentTimestamp - (moment(downloadedAt).unix() * 1000);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default timeSinceUpdateWasDownloadedInjectable;
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { IComputedValue } from "mobx";
|
||||
|
||||
export interface AppPreferenceTabRegistration {
|
||||
title: string;
|
||||
id: string;
|
||||
orderNumber?: number;
|
||||
visible?: IComputedValue<boolean>;
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import appPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/app/app-preferences-route.injectable";
|
||||
import { Application } from "./application";
|
||||
|
||||
const appPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "app-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(appPreferencesRouteInjectable),
|
||||
Component: Application,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default appPreferencesRouteComponentInjectable;
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type React from "react";
|
||||
|
||||
export interface AppPreferenceComponents {
|
||||
Hint: React.ComponentType<any>;
|
||||
Input: React.ComponentType<any>;
|
||||
}
|
||||
|
||||
export interface AppPreferenceRegistration {
|
||||
title: string;
|
||||
id?: string;
|
||||
showInPreferencesTab?: string;
|
||||
components: AppPreferenceComponents;
|
||||
}
|
||||
|
||||
export interface RegisteredAppPreference extends AppPreferenceRegistration {
|
||||
id: string;
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import rendererExtensionsInjectable from "../../../../extensions/renderer-extensions.injectable";
|
||||
import { getAppPreferences } from "./get-app-preferences";
|
||||
|
||||
const appPreferencesInjectable = getInjectable({
|
||||
id: "app-preferences",
|
||||
|
||||
instantiate: (di) =>
|
||||
getAppPreferences({
|
||||
extensions: di.inject(rendererExtensionsInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
export default appPreferencesInjectable;
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { computed } from "mobx";
|
||||
import type { LensRendererExtension } from "../../../../extensions/lens-renderer-extension";
|
||||
import type { AppPreferenceRegistration, RegisteredAppPreference } from "./app-preference-registration";
|
||||
|
||||
interface Dependencies {
|
||||
extensions: IComputedValue<LensRendererExtension[]>;
|
||||
}
|
||||
|
||||
function getRegisteredItem(item: AppPreferenceRegistration): RegisteredAppPreference {
|
||||
return {
|
||||
id: item.id || item.title.toLowerCase().replace(/[^0-9a-zA-Z]+/g, "-"),
|
||||
...item,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export const getAppPreferences = ({ extensions }: Dependencies) => {
|
||||
return computed(() => (
|
||||
extensions.get()
|
||||
.flatMap((extension) => extension.appPreferences)
|
||||
.map(getRegisteredItem)
|
||||
));
|
||||
};
|
||||
@@ -1,193 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { Select } from "../select";
|
||||
import type { ThemeStore } from "../../themes/store";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { Input } from "../input";
|
||||
import { Switch } from "../switch";
|
||||
import moment from "moment-timezone";
|
||||
import { defaultExtensionRegistryUrl, defaultLocaleTimezone, defaultExtensionRegistryUrlLocation } from "../../../common/user-store/preferences-helpers";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { runInAction } from "mobx";
|
||||
import { isUrl } from "../input/input_validators";
|
||||
import { ExtensionSettings } from "./extension-settings";
|
||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import appPreferencesInjectable from "./app-preferences/app-preferences.injectable";
|
||||
import { Preferences } from "./preferences";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import themeStoreInjectable from "../../themes/store.injectable";
|
||||
import { defaultThemeId } from "../../../common/vars";
|
||||
import { updateChannels } from "../../../common/application-update/update-channels";
|
||||
import { map, toPairs } from "lodash/fp";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import type { SelectedUpdateChannel } from "../../../common/application-update/selected-update-channel/selected-update-channel.injectable";
|
||||
import selectedUpdateChannelInjectable from "../../../common/application-update/selected-update-channel/selected-update-channel.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
appPreferenceItems: IComputedValue<RegisteredAppPreference[]>;
|
||||
userStore: UserStore;
|
||||
themeStore: ThemeStore;
|
||||
selectedUpdateChannel: SelectedUpdateChannel;
|
||||
}
|
||||
|
||||
const timezoneOptions = moment.tz.names()
|
||||
.map(timezone => ({
|
||||
value: timezone,
|
||||
label: timezone.replace("_", " "),
|
||||
}));
|
||||
|
||||
const updateChannelOptions = pipeline(
|
||||
toPairs(updateChannels),
|
||||
|
||||
map(([, channel]) => ({
|
||||
value: channel.id,
|
||||
label: channel.label,
|
||||
})),
|
||||
);
|
||||
|
||||
const extensionInstallRegistryOptions = [
|
||||
{
|
||||
value: "default",
|
||||
label: "Default Url",
|
||||
},
|
||||
{
|
||||
value: "npmrc",
|
||||
label: "Global .npmrc file's Url",
|
||||
},
|
||||
{
|
||||
value: "custom",
|
||||
label: "Custom Url",
|
||||
},
|
||||
] as const;
|
||||
|
||||
const NonInjectedApplication: React.FC<Dependencies> = ({ appPreferenceItems, userStore, themeStore, selectedUpdateChannel }) => {
|
||||
const [customUrl, setCustomUrl] = React.useState(userStore.extensionRegistryUrl.customUrl || "");
|
||||
const themeOptions = [
|
||||
{
|
||||
value: "system", // TODO: replace with a sentinal value that isn't string (and serialize it differently)
|
||||
label: "Sync with computer",
|
||||
},
|
||||
...Array.from(themeStore.themes, ([themeId, { name }]) => ({
|
||||
value: themeId,
|
||||
label: name,
|
||||
})),
|
||||
];
|
||||
const extensionSettings = appPreferenceItems.get()
|
||||
.filter((preference) => preference.showInPreferencesTab === "application");
|
||||
|
||||
return (
|
||||
<Preferences data-testid="application-preferences-page">
|
||||
<section id="application">
|
||||
<h2 data-testid="application-header">Application</h2>
|
||||
<section id="appearance">
|
||||
<SubTitle title="Theme" />
|
||||
<Select
|
||||
id="theme-input"
|
||||
options={themeOptions}
|
||||
value={userStore.colorTheme}
|
||||
onChange={value => userStore.colorTheme = value?.value ?? defaultThemeId}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<hr/>
|
||||
|
||||
<section id="extensionRegistryUrl">
|
||||
<SubTitle title="Extension Install Registry" />
|
||||
<Select
|
||||
id="extension-install-registry-input"
|
||||
options={extensionInstallRegistryOptions}
|
||||
value={userStore.extensionRegistryUrl.location}
|
||||
onChange={value => runInAction(() => {
|
||||
userStore.extensionRegistryUrl.location = value?.value ?? defaultExtensionRegistryUrlLocation;
|
||||
|
||||
if (userStore.extensionRegistryUrl.location === "custom") {
|
||||
userStore.extensionRegistryUrl.customUrl = "";
|
||||
}
|
||||
})}
|
||||
themeName="lens"
|
||||
/>
|
||||
<p className="mt-4 mb-5 leading-relaxed">
|
||||
{"This setting is to change the registry URL for installing extensions by name. "}
|
||||
{`If you are unable to access the default registry (${defaultExtensionRegistryUrl}) you can change it in your `}
|
||||
<b>.npmrc</b>
|
||||
{" file or in the input below."}
|
||||
</p>
|
||||
|
||||
<Input
|
||||
theme="round-black"
|
||||
validators={isUrl}
|
||||
value={customUrl}
|
||||
onChange={setCustomUrl}
|
||||
onBlur={() => userStore.extensionRegistryUrl.customUrl = customUrl}
|
||||
placeholder="Custom Extension Registry URL..."
|
||||
disabled={userStore.extensionRegistryUrl.location !== "custom"}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section id="other">
|
||||
<SubTitle title="Start-up" />
|
||||
<Switch checked={userStore.openAtLogin} onChange={() => userStore.openAtLogin = !userStore.openAtLogin}>
|
||||
Automatically start Lens on login
|
||||
</Switch>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
{extensionSettings.map(setting => (
|
||||
<ExtensionSettings
|
||||
key={setting.id}
|
||||
setting={setting}
|
||||
size="normal"
|
||||
/>
|
||||
))}
|
||||
|
||||
<section id="update-channel">
|
||||
<SubTitle title="Update Channel" />
|
||||
<Select
|
||||
id="update-channel-input"
|
||||
options={updateChannelOptions}
|
||||
value={selectedUpdateChannel.value.get().id}
|
||||
onChange={(selected) => selectedUpdateChannel.setValue(selected?.value) }
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section id="locale">
|
||||
<SubTitle title="Locale Timezone" />
|
||||
<Select
|
||||
id="timezone-input"
|
||||
options={timezoneOptions}
|
||||
value={userStore.localeTimezone}
|
||||
onChange={value => userStore.localeTimezone = value?.value ?? defaultLocaleTimezone}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
</Preferences>
|
||||
);
|
||||
};
|
||||
|
||||
export const Application = withInjectables<Dependencies>(
|
||||
observer(NonInjectedApplication),
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
appPreferenceItems: di.inject(appPreferencesInjectable),
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
themeStore: di.inject(themeStoreInjectable),
|
||||
selectedUpdateChannel: di.inject(selectedUpdateChannelInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import observableHistoryInjectable from "../../navigation/observable-history.injectable";
|
||||
import navigateToFrontPageInjectable from "../../../common/front-end-routing/navigate-to-front-page.injectable";
|
||||
|
||||
const closePreferencesInjectable = getInjectable({
|
||||
id: "close-preferences",
|
||||
|
||||
instantiate: (di) => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
const navigateToFrontPage = di.inject(navigateToFrontPageInjectable);
|
||||
|
||||
return () => {
|
||||
if (observableHistory.length <= 1) {
|
||||
navigateToFrontPage();
|
||||
} else {
|
||||
observableHistory.goBack();
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default closePreferencesInjectable;
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
|
||||
|
||||
const defaultShellInjectable = getInjectable({
|
||||
id: "default-shell",
|
||||
|
||||
instantiate: (di) => {
|
||||
const isWindows = di.inject(isWindowsInjectable);
|
||||
|
||||
return (
|
||||
process.env.SHELL ||
|
||||
process.env.PTYSHELL ||
|
||||
(isWindows ? "powershell.exe" : "System default shell")
|
||||
);
|
||||
},
|
||||
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default defaultShellInjectable;
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import { Editor } from "./editor";
|
||||
import editorPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/editor/editor-preferences-route.injectable";
|
||||
|
||||
const editorPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "editor-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(editorPreferencesRouteInjectable),
|
||||
Component: Editor,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default editorPreferencesRouteComponentInjectable;
|
||||
@@ -1,127 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { Switch } from "../switch";
|
||||
import { Select } from "../select";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { SubHeader } from "../layout/sub-header";
|
||||
import { Input, InputValidators } from "../input";
|
||||
import { Preferences } from "./preferences";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import { defaultEditorConfig } from "../../../common/user-store/preferences-helpers";
|
||||
import { capitalize } from "lodash";
|
||||
|
||||
interface Dependencies {
|
||||
userStore: UserStore;
|
||||
}
|
||||
|
||||
const minimapPositionOptions = (["left", "right"] as const)
|
||||
.map(side => ({
|
||||
value: side,
|
||||
label: side,
|
||||
}));
|
||||
const lineNumberOptions = ([
|
||||
"on",
|
||||
"off",
|
||||
"relative",
|
||||
"interval",
|
||||
] as const).map(lineNumbers => ({
|
||||
value: lineNumbers,
|
||||
label: capitalize(lineNumbers),
|
||||
}));
|
||||
|
||||
const NonInjectedEditor = observer(({ userStore }: Dependencies) => {
|
||||
const editorConfiguration = userStore.editorConfiguration;
|
||||
|
||||
return (
|
||||
<Preferences data-testid="editor-preferences-page">
|
||||
<section id="editor">
|
||||
<h2 data-testid="editor-configuration-header">Editor configuration</h2>
|
||||
|
||||
<SubTitle title="Minimap"/>
|
||||
<section>
|
||||
<div className="flex gaps justify-space-between">
|
||||
<div className="flex gaps align-center">
|
||||
<Switch
|
||||
checked={editorConfiguration.minimap.enabled}
|
||||
onChange={() => editorConfiguration.minimap.enabled = !editorConfiguration.minimap.enabled}
|
||||
>
|
||||
Show minimap
|
||||
</Switch>
|
||||
</div>
|
||||
<div className="flex gaps align-center">
|
||||
<SubHeader compact>Position</SubHeader>
|
||||
<Select
|
||||
id="minimap-input"
|
||||
themeName="lens"
|
||||
options={minimapPositionOptions}
|
||||
value={editorConfiguration.minimap.side}
|
||||
onChange={option => editorConfiguration.minimap.side = option?.value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Line numbers"/>
|
||||
<Select
|
||||
id="editor-line-numbers-input"
|
||||
options={lineNumberOptions}
|
||||
value={editorConfiguration.lineNumbers}
|
||||
onChange={option => editorConfiguration.lineNumbers = option?.value ?? defaultEditorConfig.lineNumbers}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Tab size" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="number"
|
||||
min={1}
|
||||
validators={InputValidators.isNumber}
|
||||
value={editorConfiguration.tabSize.toString()}
|
||||
onChange={value => editorConfiguration.tabSize = Number(value)}
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<SubTitle title="Font size" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="number"
|
||||
min={10}
|
||||
validators={InputValidators.isNumber}
|
||||
value={editorConfiguration.fontSize.toString()}
|
||||
onChange={value => editorConfiguration.fontSize = Number(value)}
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<SubTitle title="Font family" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="text"
|
||||
value={editorConfiguration.fontFamily}
|
||||
onChange={value => editorConfiguration.fontFamily = value}
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
</Preferences>
|
||||
);
|
||||
});
|
||||
|
||||
export const Editor = withInjectables<Dependencies>(
|
||||
NonInjectedEditor,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { filter, map } from "lodash/fp";
|
||||
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { extensionPreferenceItemInjectionToken } from "./extension-preference-items-injection-token";
|
||||
|
||||
const extensionPreferenceItemRegistratorInjectable = getInjectable({
|
||||
id: "extension-preference-item-registrator",
|
||||
|
||||
instantiate:
|
||||
() =>
|
||||
(ext) => {
|
||||
const extension = ext as LensRendererExtension;
|
||||
|
||||
return pipeline(
|
||||
extension.appPreferences,
|
||||
|
||||
filter(
|
||||
(registration) => !registration.showInPreferencesTab,
|
||||
),
|
||||
|
||||
map((registration) => {
|
||||
const id = `extension-preferences-item-${registration.id}-for-extension-${extension.sanitizedExtensionId}`;
|
||||
|
||||
return getInjectable({
|
||||
id,
|
||||
injectionToken: extensionPreferenceItemInjectionToken,
|
||||
|
||||
instantiate: () => ({
|
||||
id: registration.id || id,
|
||||
title: registration.title,
|
||||
extension,
|
||||
|
||||
components: {
|
||||
Hint: registration.components.Hint,
|
||||
Input: registration.components.Input,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
||||
injectionToken: extensionRegistratorInjectionToken,
|
||||
});
|
||||
|
||||
export default extensionPreferenceItemRegistratorInjectable;
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||
|
||||
interface ExtensionPreferenceItem extends RegisteredAppPreference {
|
||||
extension: LensRendererExtension;
|
||||
}
|
||||
|
||||
export const extensionPreferenceItemInjectionToken = getInjectionToken<ExtensionPreferenceItem>({
|
||||
id: "extension-preference-item-injection-token",
|
||||
});
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
|
||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||
|
||||
export const extensionPreferenceItemInjectionToken = getInjectionToken<RegisteredAppPreference>({
|
||||
id: "extension-preference-item-injection-token",
|
||||
});
|
||||
|
||||
const extensionsPreferenceItemsInjectable = getInjectable({
|
||||
id: "extension-preference-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const computedInjectMany = di.inject(computedInjectManyInjectable);
|
||||
|
||||
return computedInjectMany(extensionPreferenceItemInjectionToken);
|
||||
},
|
||||
});
|
||||
|
||||
export default extensionsPreferenceItemsInjectable;
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import extensionPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
|
||||
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
|
||||
import routePathParametersInjectable from "../../routes/route-path-parameters.injectable";
|
||||
import { getExtensionPreferenceItems } from "./get-extension-preference-items";
|
||||
|
||||
const extensionPreferencesModelInjectable = getInjectable({
|
||||
id: "extension-preferences-model",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(extensionPreferencesRouteInjectable);
|
||||
const pathParameters = di.inject(routePathParametersInjectable, route);
|
||||
const extensions = di.inject(rendererExtensionsInjectable);
|
||||
|
||||
return computed(() => {
|
||||
const { extensionId, tabId } = pathParameters.get();
|
||||
const targetExtension = extensions.get().find((extension) => extension.sanitizedExtensionId === extensionId);
|
||||
const targetAppTab = targetExtension?.appPreferenceTabs.find(tab => tab.id === tabId);
|
||||
const preferencePageTitle = targetAppTab?.title || `${targetExtension?.manifest.name || "Extension"} preferences`;
|
||||
|
||||
return {
|
||||
extensionName: targetExtension?.manifest.name,
|
||||
preferenceItems: getExtensionPreferenceItems(targetExtension, tabId),
|
||||
preferencePageTitle,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default extensionPreferencesModelInjectable;
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import extensionPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
|
||||
import { Extensions } from "./extensions";
|
||||
|
||||
const extensionPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "extension-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(extensionPreferencesRouteInjectable),
|
||||
Component: Extensions,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default extensionPreferencesRouteComponentInjectable;
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import type { AppPreferenceRegistration } from "./app-preferences/app-preference-registration";
|
||||
import type { DOMAttributes } from "react";
|
||||
import React from "react";
|
||||
import { cssNames } from "../../../renderer/utils";
|
||||
|
||||
export interface ExtensionSettingsProps extends DOMAttributes<HTMLDivElement> {
|
||||
setting: AppPreferenceRegistration;
|
||||
size: "small" | "normal";
|
||||
}
|
||||
|
||||
export function ExtensionSettings({ setting, size, ...props }: ExtensionSettingsProps) {
|
||||
const {
|
||||
title,
|
||||
id,
|
||||
components: { Hint, Input },
|
||||
} = setting;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<section
|
||||
id={id}
|
||||
className={cssNames(size)}
|
||||
{...props}
|
||||
>
|
||||
<SubTitle title={title} />
|
||||
<Input />
|
||||
<div className="hint">
|
||||
<Hint />
|
||||
</div>
|
||||
</section>
|
||||
<hr className={cssNames(size)} />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { filter, map } from "lodash/fp";
|
||||
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { telemetryPreferenceItemInjectionToken } from "./telemetry-preference-items.injectable";
|
||||
|
||||
const extensionTelemetryPreferenceItemRegistratorInjectable = getInjectable({
|
||||
id: "extension-telemetry-preference-item-registrator",
|
||||
|
||||
instantiate:
|
||||
() =>
|
||||
(ext) => {
|
||||
const extension = ext as LensRendererExtension;
|
||||
|
||||
return pipeline(
|
||||
extension.appPreferences,
|
||||
|
||||
filter(
|
||||
(registration) => registration.showInPreferencesTab === "telemetry",
|
||||
),
|
||||
|
||||
map((registration) => {
|
||||
const id = `telemetry-preferences-item-${registration.id}-for-extension-${extension.sanitizedExtensionId}`;
|
||||
|
||||
return getInjectable({
|
||||
id,
|
||||
injectionToken: telemetryPreferenceItemInjectionToken,
|
||||
|
||||
instantiate: () => ({
|
||||
id: registration.id || id,
|
||||
title: registration.title,
|
||||
|
||||
components: {
|
||||
Hint: registration.components.Hint,
|
||||
Input: registration.components.Input,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
||||
injectionToken: extensionRegistratorInjectionToken,
|
||||
});
|
||||
|
||||
export default extensionTelemetryPreferenceItemRegistratorInjectable;
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||
import extensionPreferencesModelInjectable from "./extension-preference-model.injectable";
|
||||
import { ExtensionSettings } from "./extension-settings";
|
||||
import { Preferences } from "./preferences";
|
||||
|
||||
interface Dependencies {
|
||||
model: IComputedValue<{
|
||||
preferenceItems: RegisteredAppPreference[];
|
||||
extensionName?: string;
|
||||
preferencePageTitle?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
const NonInjectedExtensions = ({ model }: Dependencies) => {
|
||||
const { extensionName, preferenceItems, preferencePageTitle } = model.get();
|
||||
|
||||
return (
|
||||
<Preferences data-testid="extension-preferences-page">
|
||||
<section id="extensions">
|
||||
<h2 data-testid="extension-preferences-page-title">
|
||||
{preferencePageTitle}
|
||||
</h2>
|
||||
{!extensionName && (
|
||||
<div
|
||||
className="flex items-center"
|
||||
data-testid="error-for-extension-not-being-present"
|
||||
>
|
||||
No extension found
|
||||
</div>
|
||||
)}
|
||||
{preferenceItems.map((preferenceItem, index) => (
|
||||
<ExtensionSettings
|
||||
key={`${preferenceItem.id}-${index}`}
|
||||
setting={preferenceItem}
|
||||
size="small"
|
||||
data-testid={`extension-preference-item-for-${preferenceItem.id}`}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
</Preferences>
|
||||
);
|
||||
};
|
||||
|
||||
export const Extensions = withInjectables<Dependencies>(
|
||||
observer(NonInjectedExtensions),
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
model: di.inject(extensionPreferencesModelInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||
|
||||
export function getExtensionPreferenceItems(extension?: LensRendererExtension, tabId?: string): RegisteredAppPreference[] {
|
||||
if (!extension) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const preferences = extension.appPreferences.map(preference => ({
|
||||
id: preference.id || preference.title,
|
||||
...preference,
|
||||
}));
|
||||
|
||||
if (tabId) {
|
||||
return preferences.filter(preference => preference.showInPreferencesTab == tabId);
|
||||
}
|
||||
|
||||
return preferences.filter(preference => !preference.showInPreferencesTab);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./preferences";
|
||||
@@ -1,217 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import fse from "fs-extra";
|
||||
import { computed, makeObservable, observable, reaction } from "mobx";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { Notice } from "../+extensions/notice";
|
||||
import type { KubeconfigSyncEntry, KubeconfigSyncValue, UserStore } from "../../../common/user-store";
|
||||
import { iter, tuple } from "../../utils";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { PathPicker } from "../path-picker/path-picker";
|
||||
import { Spinner } from "../spinner";
|
||||
import { RemovableItem } from "./removable-item";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import type { Logger } from "../../../common/logger";
|
||||
|
||||
interface SyncInfo {
|
||||
type: "file" | "folder" | "unknown";
|
||||
}
|
||||
|
||||
interface Entry extends Value {
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
interface Value {
|
||||
data: KubeconfigSyncValue;
|
||||
info: SyncInfo;
|
||||
}
|
||||
|
||||
async function getMapEntry({ filePath, ...data }: KubeconfigSyncEntry, logger: Logger): Promise<[string, Value]> {
|
||||
try {
|
||||
// stat follows the stat(2) linux syscall spec, namely it follows symlinks
|
||||
const stats = await fse.stat(filePath);
|
||||
|
||||
if (stats.isFile()) {
|
||||
return [filePath, { info: { type: "file" }, data }];
|
||||
}
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return [filePath, { info: { type: "folder" }, data }];
|
||||
}
|
||||
|
||||
logger.warn("[KubeconfigSyncs]: unknown stat entry", { stats });
|
||||
|
||||
return [filePath, { info: { type: "unknown" }, data }];
|
||||
} catch (error) {
|
||||
logger.warn(`[KubeconfigSyncs]: failed to stat entry: ${error}`, { error });
|
||||
|
||||
return [filePath, { info: { type: "unknown" }, data }];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllEntries(filePaths: string[], logger: Logger): Promise<[string, Value][]> {
|
||||
return Promise.all(filePaths.map(filePath => getMapEntry({ filePath }, logger)));
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
userStore: UserStore;
|
||||
isWindows: boolean;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedKubeconfigSyncs extends React.Component<Dependencies> {
|
||||
syncs = observable.map<string, Value>();
|
||||
@observable loaded = false;
|
||||
|
||||
constructor(props: Dependencies) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const mapEntries = await Promise.all(
|
||||
iter.map(
|
||||
this.props.userStore.syncKubeconfigEntries,
|
||||
([filePath, ...value]) => getMapEntry({ filePath, ...value }, this.props.logger),
|
||||
),
|
||||
);
|
||||
|
||||
this.syncs.replace(mapEntries);
|
||||
this.loaded = true;
|
||||
|
||||
disposeOnUnmount(this, [
|
||||
reaction(
|
||||
() => Array.from(this.syncs.entries(), ([filePath, { data }]) => tuple.from(filePath, data)),
|
||||
syncs => {
|
||||
this.props.userStore.syncKubeconfigEntries.replace(syncs);
|
||||
},
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
@computed get syncsList(): Entry[] | undefined {
|
||||
if (!this.loaded) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Array.from(this.syncs.entries(), ([filePath, value]) => ({ filePath, ...value }));
|
||||
}
|
||||
|
||||
onPick = async (filePaths: string[]) => {
|
||||
this.syncs.merge(await getAllEntries(filePaths, this.props.logger));
|
||||
};
|
||||
|
||||
getIconName(entry: Entry) {
|
||||
switch (entry.info.type) {
|
||||
case "file":
|
||||
return "description";
|
||||
case "folder":
|
||||
return "folder";
|
||||
case "unknown":
|
||||
return "help_outline";
|
||||
}
|
||||
}
|
||||
|
||||
renderEntry = (entry: Entry) => {
|
||||
return (
|
||||
<RemovableItem
|
||||
key={entry.filePath}
|
||||
onRemove={() => this.syncs.delete(entry.filePath)}
|
||||
className="mt-3"
|
||||
icon={this.getIconName(entry)}
|
||||
>
|
||||
<div className="flex-grow break-all">
|
||||
{entry.filePath}
|
||||
</div>
|
||||
</RemovableItem>
|
||||
);
|
||||
};
|
||||
|
||||
renderEntries() {
|
||||
const entries = this.syncsList;
|
||||
|
||||
if (!entries) {
|
||||
return (
|
||||
<div className="loading-spinner">
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!entries.length) {
|
||||
return (
|
||||
<Notice className="mt-3">
|
||||
<div className="flex-grow text-center">No files and folders have been synced yet</div>
|
||||
</Notice>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{entries.map(this.renderEntry)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderSyncButtons() {
|
||||
if (this.props.isWindows) {
|
||||
return (
|
||||
<div className="flex gaps align-center mb-5">
|
||||
<PathPicker
|
||||
label="Sync file(s)"
|
||||
onPick={this.onPick}
|
||||
buttonLabel="Sync"
|
||||
properties={["showHiddenFiles", "multiSelections", "openFile"]}
|
||||
/>
|
||||
<span>or</span>
|
||||
<PathPicker
|
||||
label="Sync folder(s)"
|
||||
onPick={this.onPick}
|
||||
buttonLabel="Sync"
|
||||
properties={["showHiddenFiles", "multiSelections", "openDirectory"]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="self-start mb-5">
|
||||
<PathPicker
|
||||
label="Sync Files and Folders"
|
||||
onPick={this.onPick}
|
||||
buttonLabel="Sync"
|
||||
properties={["showHiddenFiles", "multiSelections", "openFile", "openDirectory"]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
{this.renderSyncButtons()}
|
||||
<SubTitle title="Synced Items" className="pt-5"/>
|
||||
{this.renderEntries()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const KubeconfigSyncs = withInjectables<Dependencies>(
|
||||
NonInjectedKubeconfigSyncs,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
isWindows: di.inject(isWindowsInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -1,109 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Input, InputValidators } from "../input";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { Select } from "../select";
|
||||
import { Switch } from "../switch";
|
||||
import { defaultPackageMirror, packageMirrors } from "../../../common/user-store/preferences-helpers";
|
||||
import directoryForBinariesInjectable from "../../../common/app-paths/directory-for-binaries/directory-for-binaries.injectable";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import directoryForKubectlBinariesInjectable from "../../../common/app-paths/directory-for-kubectl-binaries/directory-for-kubectl-binaries.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
defaultPathForGeneralBinaries: string;
|
||||
defaultPathForKubectlBinaries: string;
|
||||
userStore: UserStore;
|
||||
}
|
||||
const downloadMirrorOptions = Array.from(packageMirrors, ([name, mirror]) => ({
|
||||
value: name,
|
||||
label: mirror.label,
|
||||
isDisabled: !mirror.platforms.has(process.platform),
|
||||
}));
|
||||
|
||||
const NonInjectedKubectlBinaries= observer(({
|
||||
defaultPathForGeneralBinaries,
|
||||
defaultPathForKubectlBinaries,
|
||||
userStore,
|
||||
}: Dependencies) => {
|
||||
const [downloadPath, setDownloadPath] = useState(userStore.downloadBinariesPath || "");
|
||||
const [binariesPath, setBinariesPath] = useState(userStore.kubectlBinariesPath || "");
|
||||
const pathValidator = downloadPath ? InputValidators.isPath : undefined;
|
||||
|
||||
const save = () => {
|
||||
userStore.downloadBinariesPath = downloadPath;
|
||||
userStore.kubectlBinariesPath = binariesPath;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<SubTitle title="Kubectl binary download" />
|
||||
<Switch
|
||||
checked={userStore.downloadKubectlBinaries}
|
||||
onChange={() => userStore.downloadKubectlBinaries = !userStore.downloadKubectlBinaries}
|
||||
>
|
||||
Download kubectl binaries matching the Kubernetes cluster version
|
||||
</Switch>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Download mirror" />
|
||||
<Select
|
||||
id="download-mirror-input"
|
||||
placeholder="Download mirror for kubectl"
|
||||
options={downloadMirrorOptions}
|
||||
value={userStore.downloadMirror}
|
||||
onChange={option => userStore.downloadMirror = option?.value ?? defaultPackageMirror}
|
||||
isDisabled={!userStore.downloadKubectlBinaries}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Directory for binaries" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
value={downloadPath}
|
||||
placeholder={defaultPathForGeneralBinaries}
|
||||
validators={pathValidator}
|
||||
onChange={setDownloadPath}
|
||||
onBlur={save}
|
||||
disabled={!userStore.downloadKubectlBinaries}
|
||||
/>
|
||||
<div className="hint">The directory to download binaries into.</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Path to kubectl binary" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
placeholder={defaultPathForKubectlBinaries}
|
||||
value={binariesPath}
|
||||
validators={pathValidator}
|
||||
onChange={setBinariesPath}
|
||||
onBlur={save}
|
||||
disabled={userStore.downloadKubectlBinaries}
|
||||
/>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export const KubectlBinaries = withInjectables<Dependencies>(
|
||||
NonInjectedKubectlBinaries,
|
||||
{
|
||||
getProps: (di) => ({
|
||||
defaultPathForGeneralBinaries: di.inject(directoryForBinariesInjectable),
|
||||
defaultPathForKubectlBinaries: di.inject(directoryForKubectlBinariesInjectable),
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import kubernetesPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/kubernetes/kubernetes-preferences-route.injectable";
|
||||
import { Kubernetes } from "./kubernetes";
|
||||
|
||||
const kubernetesPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "kubernetes-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(kubernetesPreferencesRouteInjectable),
|
||||
Component: Kubernetes,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default kubernetesPreferencesRouteComponentInjectable;
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { HelmCharts } from "./kubernetes/helm-charts/helm-charts";
|
||||
import { KubeconfigSyncs } from "./kubeconfig-syncs";
|
||||
import { KubectlBinaries } from "./kubectl-binaries";
|
||||
import { Preferences } from "./preferences";
|
||||
|
||||
export const Kubernetes = observer(() => (
|
||||
<Preferences data-testid="kubernetes-preferences-page">
|
||||
<section id="kubernetes">
|
||||
<section id="kubectl">
|
||||
<h2 data-testid="kubernetes-header">Kubernetes</h2>
|
||||
<KubectlBinaries />
|
||||
</section>
|
||||
<hr />
|
||||
<section id="kube-sync">
|
||||
<h2 data-testid="kubernetes-sync-header">Kubeconfig Syncs</h2>
|
||||
<KubeconfigSyncs />
|
||||
</section>
|
||||
<hr />
|
||||
<section id="helm">
|
||||
<h2>Helm Charts</h2>
|
||||
<HelmCharts />
|
||||
</section>
|
||||
</section>
|
||||
</Preferences>
|
||||
));
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { asyncComputed } from "@ogre-tools/injectable-react";
|
||||
import { getActiveHelmRepositoriesChannel } from "../../../../../common/helm/get-active-helm-repositories-channel";
|
||||
import { requestFromChannelInjectionToken } from "../../../../../common/utils/channel/request-from-channel-injection-token";
|
||||
import showErrorNotificationInjectable from "../../../notifications/show-error-notification.injectable";
|
||||
import helmRepositoriesErrorStateInjectable from "./helm-repositories-error-state.injectable";
|
||||
import { runInAction } from "mobx";
|
||||
|
||||
const activeHelmRepositoriesInjectable = getInjectable({
|
||||
id: "active-helm-repositories",
|
||||
|
||||
instantiate: (di) => {
|
||||
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
|
||||
const showErrorNotification = di.inject(showErrorNotificationInjectable);
|
||||
const helmRepositoriesErrorState = di.inject(helmRepositoriesErrorStateInjectable);
|
||||
|
||||
return asyncComputed(async () => {
|
||||
const result = await requestFromChannel(getActiveHelmRepositoriesChannel);
|
||||
|
||||
if (result.callWasSuccessful) {
|
||||
return result.response;
|
||||
} else {
|
||||
showErrorNotification(result.error);
|
||||
|
||||
runInAction(() =>
|
||||
helmRepositoriesErrorState.set({
|
||||
controlsAreShown: false,
|
||||
errorMessage: result.error,
|
||||
}),
|
||||
);
|
||||
|
||||
return [];
|
||||
}
|
||||
}, []);
|
||||
},
|
||||
});
|
||||
|
||||
export default activeHelmRepositoriesInjectable;
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
.AddHelmRepoDialog {
|
||||
--flex-gap: #{$margin * 1.5};
|
||||
|
||||
.accordion {
|
||||
font-weight: bold;
|
||||
align-self: flex-start;
|
||||
padding: 0;
|
||||
&:focus:not(:active) {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.Icon {
|
||||
--color-active: black;
|
||||
}
|
||||
|
||||
.fields-title {
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
align-items: center;
|
||||
gap: $margin * 0.5;
|
||||
}
|
||||
}
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./add-helm-repo-dialog.scss";
|
||||
|
||||
import React from "react";
|
||||
import { Wizard, WizardStep } from "../../../../wizard";
|
||||
import { Input } from "../../../../input";
|
||||
import { systemName, isUrl } from "../../../../input/input_validators";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import customHelmRepoInjectable from "./custom-helm-repo.injectable";
|
||||
import type { HelmRepo } from "../../../../../../common/helm/helm-repo";
|
||||
import { observer } from "mobx-react";
|
||||
import type { IObservableValue } from "mobx";
|
||||
import { action } from "mobx";
|
||||
import submitCustomHelmRepositoryInjectable from "./submit-custom-helm-repository.injectable";
|
||||
import hideDialogForAddingCustomHelmRepositoryInjectable from "./dialog-visibility/hide-dialog-for-adding-custom-helm-repository.injectable";
|
||||
import { Button } from "../../../../button";
|
||||
import { Icon } from "../../../../icon";
|
||||
import maximalCustomHelmRepoOptionsAreShownInjectable from "./maximal-custom-helm-repo-options-are-shown.injectable";
|
||||
import { SubTitle } from "../../../../layout/sub-title";
|
||||
import { Checkbox } from "../../../../checkbox";
|
||||
import { HelmFileInput } from "./helm-file-input/helm-file-input";
|
||||
|
||||
interface Dependencies {
|
||||
helmRepo: HelmRepo;
|
||||
hideDialog: () => void;
|
||||
submitCustomRepository: (repository: HelmRepo) => Promise<void>;
|
||||
maximalOptionsAreShown: IObservableValue<boolean>;
|
||||
}
|
||||
|
||||
const NonInjectedActivationOfCustomHelmRepositoryDialogContent = observer(({ helmRepo, submitCustomRepository, maximalOptionsAreShown, hideDialog } : Dependencies) => (
|
||||
<Wizard header={<h5>Add custom Helm Repo</h5>} done={hideDialog}>
|
||||
<WizardStep
|
||||
contentClass="flow column"
|
||||
nextLabel="Add"
|
||||
next={() => submitCustomRepository(helmRepo)}
|
||||
testIdForNext="custom-helm-repository-submit-button"
|
||||
testIdForPrev="custom-helm-repository-cancel-button"
|
||||
>
|
||||
<div className="flex column gaps" data-testid="add-custom-helm-repository-dialog">
|
||||
<Input
|
||||
autoFocus
|
||||
required
|
||||
placeholder="Helm repo name"
|
||||
trim
|
||||
validators={systemName}
|
||||
value={helmRepo.name}
|
||||
onChange={action(v => helmRepo.name = v)}
|
||||
data-testid="custom-helm-repository-name-input"
|
||||
/>
|
||||
<Input
|
||||
required
|
||||
placeholder="URL"
|
||||
validators={isUrl}
|
||||
value={helmRepo.url}
|
||||
onChange={action(v => helmRepo.url = v)}
|
||||
data-testid="custom-helm-repository-url-input"
|
||||
/>
|
||||
<Button
|
||||
plain
|
||||
className="accordion"
|
||||
data-testid="toggle-maximal-options-for-custom-helm-repository-button"
|
||||
onClick={action(() => maximalOptionsAreShown.set(!maximalOptionsAreShown.get()))}
|
||||
>
|
||||
More
|
||||
<Icon
|
||||
small
|
||||
tooltip="More"
|
||||
material={maximalOptionsAreShown.get() ? "remove" : "add"}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
{maximalOptionsAreShown.get() && (
|
||||
<div data-testid="maximal-options-for-custom-helm-repository-dialog">
|
||||
<SubTitle title="Security settings" />
|
||||
|
||||
<Checkbox
|
||||
label="Skip TLS certificate checks for the repository"
|
||||
value={helmRepo.insecureSkipTlsVerify}
|
||||
onChange={action(v => {
|
||||
helmRepo.insecureSkipTlsVerify = v;
|
||||
})}
|
||||
data-testid="custom-helm-repository-verify-tls-input"
|
||||
/>
|
||||
|
||||
<HelmFileInput
|
||||
placeholder="Key file"
|
||||
value={helmRepo.keyFile || ""}
|
||||
setValue={action((value) => helmRepo.keyFile = value)}
|
||||
fileExtensions={keyExtensions}
|
||||
data-testid="custom-helm-repository-key-file-input"
|
||||
/>
|
||||
|
||||
<HelmFileInput
|
||||
placeholder="Ca file"
|
||||
value={helmRepo.caFile || ""}
|
||||
setValue={action((value) => helmRepo.caFile = value)}
|
||||
fileExtensions={certExtensions}
|
||||
data-testid="custom-helm-repository-ca-cert-file-input"
|
||||
/>
|
||||
|
||||
<HelmFileInput
|
||||
placeholder="Certificate file"
|
||||
value={helmRepo.certFile || ""}
|
||||
setValue={action((value) => helmRepo.certFile = value)}
|
||||
fileExtensions={certExtensions}
|
||||
data-testid="custom-helm-repository-cert-file-input"
|
||||
/>
|
||||
|
||||
<SubTitle title="Chart Repository Credentials" />
|
||||
|
||||
<Input
|
||||
placeholder="Username"
|
||||
value={helmRepo.username}
|
||||
onChange= {action(v => helmRepo.username = v)}
|
||||
data-testid="custom-helm-repository-username-input"
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={helmRepo.password}
|
||||
onChange={action(v => helmRepo.password = v)}
|
||||
data-testid="custom-helm-repository-password-input"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</WizardStep>
|
||||
</Wizard>
|
||||
));
|
||||
|
||||
|
||||
|
||||
export const AddingOfCustomHelmRepositoryDialogContent = withInjectables<Dependencies>(
|
||||
NonInjectedActivationOfCustomHelmRepositoryDialogContent,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
helmRepo: di.inject(customHelmRepoInjectable),
|
||||
hideDialog: di.inject(hideDialogForAddingCustomHelmRepositoryInjectable),
|
||||
submitCustomRepository: di.inject(submitCustomHelmRepositoryInjectable),
|
||||
maximalOptionsAreShown: di.inject(maximalCustomHelmRepoOptionsAreShownInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
const keyExtensions = ["key", "keystore", "jks", "p12", "pfx", "pem"];
|
||||
const certExtensions = ["crt", "cer", "ca-bundle", "p7b", "p7c", "p7s", "p12", "pfx", "pem"];
|
||||
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./add-helm-repo-dialog.scss";
|
||||
|
||||
import React from "react";
|
||||
import { Dialog } from "../../../../dialog";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { AddingOfCustomHelmRepositoryDialogContent } from "./adding-of-custom-helm-repository-dialog-content";
|
||||
import addingOfCustomHelmRepositoryDialogIsVisibleInjectable from "./dialog-visibility/adding-of-custom-helm-repository-dialog-is-visible.injectable";
|
||||
import type { IObservableValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import hideDialogForAddingCustomHelmRepositoryInjectable from "./dialog-visibility/hide-dialog-for-adding-custom-helm-repository.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
contentIsVisible: IObservableValue<boolean>;
|
||||
hideDialog: () => void;
|
||||
}
|
||||
|
||||
const NonInjectedActivationOfCustomHelmRepositoryDialog = observer(({
|
||||
contentIsVisible,
|
||||
hideDialog,
|
||||
}: Dependencies) => (
|
||||
<div>
|
||||
<Dialog
|
||||
className="AddHelmRepoDialog"
|
||||
isOpen={contentIsVisible.get()}
|
||||
close={hideDialog}
|
||||
>
|
||||
{contentIsVisible.get() && <AddingOfCustomHelmRepositoryDialogContent />}
|
||||
</Dialog>
|
||||
</div>
|
||||
));
|
||||
|
||||
|
||||
export const AddingOfCustomHelmRepositoryDialog = withInjectables<Dependencies>(
|
||||
NonInjectedActivationOfCustomHelmRepositoryDialog,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
contentIsVisible: di.inject(addingOfCustomHelmRepositoryDialogIsVisibleInjectable),
|
||||
hideDialog: di.inject(hideDialogForAddingCustomHelmRepositoryInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import React from "react";
|
||||
import { Button } from "../../../../button";
|
||||
import showDialogForAddingCustomHelmRepositoryInjectable from "./dialog-visibility/show-dialog-for-adding-custom-helm-repository.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
showDialog: () => void;
|
||||
}
|
||||
|
||||
const NonInjectedActivationOfCustomHelmRepositoryOpenButton = ({ showDialog }: Dependencies) => (
|
||||
<Button
|
||||
primary
|
||||
label="Add Custom Helm Repo"
|
||||
onClick={showDialog}
|
||||
data-testid="add-custom-helm-repo-button"
|
||||
/>
|
||||
);
|
||||
|
||||
export const AddingOfCustomHelmRepositoryOpenButton = withInjectables<Dependencies>(
|
||||
NonInjectedActivationOfCustomHelmRepositoryOpenButton,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
showDialog: di.inject(showDialogForAddingCustomHelmRepositoryInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { observable } from "mobx";
|
||||
|
||||
const customHelmRepoInjectable = getInjectable({
|
||||
id: "custom-helm-repo",
|
||||
|
||||
instantiate: () => observable({
|
||||
name: "",
|
||||
url: "",
|
||||
username: "",
|
||||
password: "",
|
||||
insecureSkipTlsVerify: false,
|
||||
caFile: "",
|
||||
keyFile: "",
|
||||
certFile: "",
|
||||
}),
|
||||
|
||||
lifecycle: lifecycleEnum.transient,
|
||||
});
|
||||
|
||||
export default customHelmRepoInjectable;
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { observable } from "mobx";
|
||||
|
||||
const addingOfCustomHelmRepositoryDialogIsVisibleInjectable = getInjectable({
|
||||
id: "adding-of-custom-helm-repository-dialog-is-visible",
|
||||
instantiate: () => observable.box(false),
|
||||
});
|
||||
|
||||
export default addingOfCustomHelmRepositoryDialogIsVisibleInjectable;
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { action } from "mobx";
|
||||
import addingOfCustomHelmRepositoryDialogIsVisibleInjectable from "./adding-of-custom-helm-repository-dialog-is-visible.injectable";
|
||||
|
||||
const hideDialogForAddingCustomHelmRepositoryInjectable = getInjectable({
|
||||
id: "hide-dialog-for-adding-custom-helm-repository",
|
||||
|
||||
instantiate: (di) => {
|
||||
const state = di.inject(addingOfCustomHelmRepositoryDialogIsVisibleInjectable);
|
||||
|
||||
return action(() => {
|
||||
state.set(false);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default hideDialogForAddingCustomHelmRepositoryInjectable;
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { action } from "mobx";
|
||||
import addingOfCustomHelmRepositoryDialogIsVisibleInjectable from "./adding-of-custom-helm-repository-dialog-is-visible.injectable";
|
||||
|
||||
const showDialogForAddingCustomHelmRepositoryInjectable = getInjectable({
|
||||
id: "show-dialog-for-adding-custom-helm-repository",
|
||||
|
||||
instantiate: (di) => {
|
||||
const state = di.inject(addingOfCustomHelmRepositoryDialogIsVisibleInjectable);
|
||||
|
||||
return action(() => {
|
||||
state.set(true);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default showDialogForAddingCustomHelmRepositoryInjectable;
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { FileFilter } from "electron";
|
||||
import { requestOpenFilePickingDialog } from "../../../../../../ipc";
|
||||
|
||||
const getFilePathsInjectable = getInjectable({
|
||||
id: "get-file-paths",
|
||||
|
||||
instantiate: () => async (fileFilter: FileFilter) =>
|
||||
await requestOpenFilePickingDialog({
|
||||
properties: ["openFile", "showHiddenFiles"],
|
||||
message: `Select file`,
|
||||
buttonLabel: `Use file`,
|
||||
filters: [fileFilter, { name: "Any", extensions: ["*"] }],
|
||||
}),
|
||||
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default getFilePathsInjectable;
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { InputValidator } from "../../../../../input";
|
||||
import { Input } from "../../../../../input";
|
||||
import { Icon } from "../../../../../icon";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import React from "react";
|
||||
import getFilePathsInjectable from "./get-file-paths.injectable";
|
||||
import type { FileFilter } from "electron";
|
||||
import isPathInjectable from "../../../../../input/validators/is-path.injectable";
|
||||
|
||||
interface HelmFileInputProps {
|
||||
placeholder: string;
|
||||
fileExtensions: string[];
|
||||
value: string;
|
||||
setValue: (value: string) => void;
|
||||
"data-testid"?: string;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
getFilePaths: (fileFilter: FileFilter) => Promise<{ canceled: boolean; filePaths: string[] }>;
|
||||
isPath: InputValidator<true>;
|
||||
}
|
||||
|
||||
const NonInjectedHelmFileInput = ({
|
||||
placeholder,
|
||||
value,
|
||||
setValue,
|
||||
fileExtensions,
|
||||
getFilePaths,
|
||||
isPath,
|
||||
"data-testid": testId,
|
||||
}: Dependencies & HelmFileInputProps) => (
|
||||
<div className="flex gaps align-center">
|
||||
<Input
|
||||
placeholder={placeholder}
|
||||
validators={isPath}
|
||||
className="box grow"
|
||||
value={value}
|
||||
onChange={(v) => setValue(v)}
|
||||
data-testid={testId}
|
||||
/>
|
||||
<Icon
|
||||
material="folder"
|
||||
|
||||
onClick={async () => {
|
||||
const { canceled, filePaths } = await getFilePaths({
|
||||
name: placeholder,
|
||||
extensions: fileExtensions,
|
||||
});
|
||||
|
||||
if (!canceled && filePaths.length) {
|
||||
setValue(filePaths[0]);
|
||||
}
|
||||
}}
|
||||
|
||||
tooltip="Browse"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const HelmFileInput = withInjectables<Dependencies, HelmFileInputProps>(
|
||||
NonInjectedHelmFileInput,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
getFilePaths: di.inject(getFilePathsInjectable),
|
||||
isPath: di.inject(isPathInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { observable } from "mobx";
|
||||
|
||||
const maximalCustomHelmRepoOptionsAreShownInjectable = getInjectable({
|
||||
id: "maximal-custom-helm-repo-options-are-shown",
|
||||
instantiate: () => observable.box(false),
|
||||
});
|
||||
|
||||
export default maximalCustomHelmRepoOptionsAreShownInjectable;
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { HelmRepo } from "../../../../../../common/helm/helm-repo";
|
||||
import addHelmRepositoryInjectable from "../adding-of-public-helm-repository/select-helm-repository/add-helm-repository.injectable";
|
||||
import hideDialogForAddingCustomHelmRepositoryInjectable from "./dialog-visibility/hide-dialog-for-adding-custom-helm-repository.injectable";
|
||||
|
||||
const submitCustomHelmRepositoryInjectable = getInjectable({
|
||||
id: "submit-custom-helm-repository",
|
||||
|
||||
instantiate: (di) => {
|
||||
const addHelmRepository = di.inject(addHelmRepositoryInjectable);
|
||||
const hideDialog = di.inject(hideDialogForAddingCustomHelmRepositoryInjectable);
|
||||
|
||||
return async (repository: HelmRepo) => {
|
||||
await addHelmRepository(repository);
|
||||
|
||||
hideDialog();
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default submitCustomHelmRepositoryInjectable;
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { IAsyncComputed } from "@ogre-tools/injectable-react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import React from "react";
|
||||
import publicHelmRepositoriesInjectable from "./public-helm-repositories/public-helm-repositories.injectable";
|
||||
import type { HelmRepo } from "../../../../../../common/helm/helm-repo";
|
||||
import type { SelectOption } from "../../../../select";
|
||||
import { Select } from "../../../../select";
|
||||
import { Icon } from "../../../../icon";
|
||||
import { observer } from "mobx-react";
|
||||
import type { SingleValue } from "react-select";
|
||||
import selectHelmRepositoryInjectable from "./select-helm-repository/select-helm-repository.injectable";
|
||||
import { matches } from "lodash/fp";
|
||||
import activeHelmRepositoriesInjectable from "../active-helm-repositories.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
publicRepositories: IAsyncComputed<HelmRepo[]>;
|
||||
activeRepositories: IAsyncComputed<HelmRepo[]>;
|
||||
selectRepository: (value: SingleValue<SelectOption<HelmRepo>>) => void;
|
||||
}
|
||||
|
||||
const NonInjectedAddingOfPublicHelmRepository = observer(({
|
||||
publicRepositories,
|
||||
activeRepositories,
|
||||
selectRepository,
|
||||
}: Dependencies) => {
|
||||
const dereferencesPublicRepositories = publicRepositories.value.get();
|
||||
const dereferencesActiveRepositories = activeRepositories.value.get();
|
||||
|
||||
const valuesAreLoading = publicRepositories.pending.get() || activeRepositories.pending.get();
|
||||
|
||||
const repositoryOptions = dereferencesPublicRepositories.map(repository => ({
|
||||
value: repository,
|
||||
label: repository.name,
|
||||
isSelected: !!dereferencesActiveRepositories.find(matches({ name: repository.name })),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Select
|
||||
id="selection-of-active-public-helm-repository"
|
||||
placeholder="Repositories"
|
||||
isLoading={valuesAreLoading}
|
||||
isDisabled={valuesAreLoading}
|
||||
options={repositoryOptions}
|
||||
onChange={selectRepository}
|
||||
value={dereferencesPublicRepositories}
|
||||
formatOptionLabel={formatOptionLabel}
|
||||
controlShouldRenderValue={false}
|
||||
className="box grow"
|
||||
themeName="lens"
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const AddingOfPublicHelmRepository = withInjectables<Dependencies>(
|
||||
NonInjectedAddingOfPublicHelmRepository,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
publicRepositories: di.inject(publicHelmRepositoriesInjectable),
|
||||
activeRepositories: di.inject(activeHelmRepositoriesInjectable),
|
||||
selectRepository: di.inject(selectHelmRepositoryInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
const formatOptionLabel = ({ value, isSelected }: SelectOption<HelmRepo>) => (
|
||||
<div className="flex gaps">
|
||||
<span>{value.name}</span>
|
||||
{isSelected && (
|
||||
<Icon
|
||||
small
|
||||
material="check"
|
||||
className="box right" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { sortBy } from "lodash/fp";
|
||||
import type { HelmRepo } from "../../../../../../../common/helm/helm-repo";
|
||||
import { customRequestPromise } from "../../../../../../../common/request";
|
||||
|
||||
const callForPublicHelmRepositoriesInjectable = getInjectable({
|
||||
id: "call-for-public-helm-repositories",
|
||||
|
||||
instantiate: () => async (): Promise<HelmRepo[]> => {
|
||||
const res = await customRequestPromise({
|
||||
uri: "https://github.com/lensapp/artifact-hub-repositories/releases/download/latest/repositories.json",
|
||||
json: true,
|
||||
resolveWithFullResponse: true,
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
const repositories = res.body as HelmRepo[];
|
||||
|
||||
return sortBy(repo => repo.name, repositories);
|
||||
},
|
||||
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default callForPublicHelmRepositoriesInjectable;
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { asyncComputed } from "@ogre-tools/injectable-react";
|
||||
import callForPublicHelmRepositoriesInjectable from "./call-for-public-helm-repositories.injectable";
|
||||
|
||||
const publicHelmRepositoriesInjectable = getInjectable({
|
||||
id: "public-helm-repositories",
|
||||
|
||||
instantiate: (di) => {
|
||||
const callForPublicHelmRepositories = di.inject(callForPublicHelmRepositoriesInjectable);
|
||||
|
||||
return asyncComputed(async () => {
|
||||
return await callForPublicHelmRepositories();
|
||||
}, []);
|
||||
},
|
||||
});
|
||||
|
||||
export default publicHelmRepositoriesInjectable;
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { HelmRepo } from "../../../../../../../common/helm/helm-repo";
|
||||
import { requestFromChannelInjectionToken } from "../../../../../../../common/utils/channel/request-from-channel-injection-token";
|
||||
import activeHelmRepositoriesInjectable from "../../active-helm-repositories.injectable";
|
||||
import showErrorNotificationInjectable from "../../../../../notifications/show-error-notification.injectable";
|
||||
import showSuccessNotificationInjectable from "../../../../../notifications/show-success-notification.injectable";
|
||||
import { addHelmRepositoryChannel } from "../../../../../../../common/helm/add-helm-repository-channel";
|
||||
|
||||
const addHelmRepositoryInjectable = getInjectable({
|
||||
id: "add-public-helm-repository",
|
||||
|
||||
instantiate: (di) => {
|
||||
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
|
||||
const activeHelmRepositories = di.inject(activeHelmRepositoriesInjectable);
|
||||
const showErrorNotification = di.inject(showErrorNotificationInjectable);
|
||||
const showSuccessNotification = di.inject(showSuccessNotificationInjectable);
|
||||
|
||||
return async (repository: HelmRepo) => {
|
||||
const result = await requestFromChannel(
|
||||
addHelmRepositoryChannel,
|
||||
repository,
|
||||
);
|
||||
|
||||
if (result.callWasSuccessful) {
|
||||
showSuccessNotification(
|
||||
`Helm repository ${repository.name} has been added.`,
|
||||
);
|
||||
|
||||
activeHelmRepositories.invalidate();
|
||||
} else {
|
||||
showErrorNotification(result.error);
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default addHelmRepositoryInjectable;
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import addHelmRepositoryInjectable from "./add-helm-repository.injectable";
|
||||
import type { SelectOption } from "../../../../../select";
|
||||
import type { HelmRepo } from "../../../../../../../common/helm/helm-repo";
|
||||
import type { SingleValue } from "react-select";
|
||||
import removeHelmRepositoryInjectable from "../../remove-helm-repository.injectable";
|
||||
|
||||
const selectHelmRepositoryInjectable = getInjectable({
|
||||
id: "select-helm-repository",
|
||||
|
||||
instantiate: (di) => {
|
||||
const addHelmRepository = di.inject(addHelmRepositoryInjectable);
|
||||
const removeHelmRepository = di.inject(removeHelmRepositoryInjectable);
|
||||
|
||||
return (selected: SingleValue<SelectOption<HelmRepo>>) => {
|
||||
if (!selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selected.isSelected) {
|
||||
addHelmRepository(selected.value);
|
||||
} else {
|
||||
removeHelmRepository(selected.value);
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default selectHelmRepositoryInjectable;
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
.repos {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: calc(var(--margin) * 2);
|
||||
}
|
||||
|
||||
.repos .repo + .repo {
|
||||
margin-top: calc(var(--margin) * 1.5);
|
||||
}
|
||||
|
||||
.contents {
|
||||
display: flex;
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
.repoName {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.repoUrl {
|
||||
color: var(--textColorDimmed);
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { HelmRepositories } from "./helm-repositories";
|
||||
import { AddingOfPublicHelmRepository } from "./adding-of-public-helm-repository/adding-of-public-helm-repository";
|
||||
import { AddingOfCustomHelmRepositoryOpenButton } from "./adding-of-custom-helm-repository/adding-of-custom-helm-repository-open-button";
|
||||
import { AddingOfCustomHelmRepositoryDialog } from "./adding-of-custom-helm-repository/adding-of-custom-helm-repository-dialog";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { HelmRepositoriesErrorState } from "./helm-repositories-error-state.injectable";
|
||||
import helmRepositoriesErrorStateInjectable from "./helm-repositories-error-state.injectable";
|
||||
import type { IObservableValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { Notice } from "../../../+extensions/notice";
|
||||
|
||||
interface Dependencies {
|
||||
helmRepositoriesErrorState: IObservableValue<HelmRepositoriesErrorState>;
|
||||
}
|
||||
|
||||
const NonInjectedHelmCharts = observer(
|
||||
({ helmRepositoriesErrorState }: Dependencies) => {
|
||||
const state = helmRepositoriesErrorState.get();
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!state.controlsAreShown && (
|
||||
<Notice>
|
||||
<div className="flex-grow text-center">{state.errorMessage}</div>
|
||||
</Notice>
|
||||
)}
|
||||
|
||||
{state.controlsAreShown && (
|
||||
<div data-testid="helm-controls">
|
||||
<div className="flex gaps">
|
||||
<AddingOfPublicHelmRepository />
|
||||
|
||||
<AddingOfCustomHelmRepositoryOpenButton />
|
||||
</div>
|
||||
|
||||
<HelmRepositories />
|
||||
|
||||
<AddingOfCustomHelmRepositoryDialog />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export const HelmCharts = withInjectables<Dependencies>(
|
||||
NonInjectedHelmCharts,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
helmRepositoriesErrorState: di.inject(helmRepositoriesErrorStateInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { observable } from "mobx";
|
||||
|
||||
export type HelmRepositoriesErrorState =
|
||||
| { controlsAreShown: true }
|
||||
| { controlsAreShown: false; errorMessage: string };
|
||||
|
||||
const helmRepositoriesErrorStateInjectable = getInjectable({
|
||||
id: "helm-repositories-error-state",
|
||||
|
||||
instantiate: () =>
|
||||
observable.box<HelmRepositoriesErrorState>({ controlsAreShown: true }),
|
||||
});
|
||||
|
||||
export default helmRepositoriesErrorStateInjectable;
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import styles from "./helm-charts.module.scss";
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
import activeHelmRepositoriesInjectable from "./active-helm-repositories.injectable";
|
||||
import type { IAsyncComputed } from "@ogre-tools/injectable-react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { Spinner } from "../../../spinner";
|
||||
import type { HelmRepo } from "../../../../../common/helm/helm-repo";
|
||||
import { RemovableItem } from "../../removable-item";
|
||||
import removeHelmRepositoryInjectable from "./remove-helm-repository.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
activeHelmRepositories: IAsyncComputed<HelmRepo[]>;
|
||||
removeRepository: (repository: HelmRepo) => Promise<void>;
|
||||
}
|
||||
|
||||
const NonInjectedActiveHelmRepositories = observer(({ activeHelmRepositories, removeRepository }: Dependencies) => {
|
||||
if (activeHelmRepositories.pending.get()) {
|
||||
return (
|
||||
<div className={styles.repos}>
|
||||
<div className="pt-5 relative">
|
||||
<Spinner center data-testid="helm-repositories-are-loading" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const repositories = activeHelmRepositories.value.get();
|
||||
|
||||
return (
|
||||
<div className={styles.repos}>
|
||||
{repositories.map((repository) => (
|
||||
<RemovableItem
|
||||
key={repository.name}
|
||||
onRemove={() => removeRepository(repository)}
|
||||
className={styles.repo}
|
||||
data-testid={`remove-helm-repository-${repository.name}`}
|
||||
>
|
||||
<div>
|
||||
<div data-testid={`helm-repository-${repository.name}`} className={styles.repoName}>
|
||||
{repository.name}
|
||||
</div>
|
||||
|
||||
<div className={styles.repoUrl}>{repository.url}</div>
|
||||
</div>
|
||||
</RemovableItem>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
export const HelmRepositories = withInjectables<Dependencies>(
|
||||
NonInjectedActiveHelmRepositories,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
activeHelmRepositories: di.inject(activeHelmRepositoriesInjectable),
|
||||
removeRepository: di.inject(removeHelmRepositoryInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { HelmRepo } from "../../../../../common/helm/helm-repo";
|
||||
import { requestFromChannelInjectionToken } from "../../../../../common/utils/channel/request-from-channel-injection-token";
|
||||
import activeHelmRepositoriesInjectable from "./active-helm-repositories.injectable";
|
||||
import { removeHelmRepositoryChannel } from "../../../../../common/helm/remove-helm-repository-channel";
|
||||
|
||||
const removePublicHelmRepositoryInjectable = getInjectable({
|
||||
id: "remove-public-helm-repository",
|
||||
|
||||
instantiate: (di) => {
|
||||
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
|
||||
const activeHelmRepositories = di.inject(activeHelmRepositoriesInjectable);
|
||||
|
||||
return async (repository: HelmRepo) => {
|
||||
await requestFromChannel(removeHelmRepositoryChannel, repository);
|
||||
|
||||
activeHelmRepositories.invalidate();
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default removePublicHelmRepositoryInjectable;
|
||||
-148
@@ -1,148 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "@testing-library/jest-dom/extend-expect";
|
||||
import type { RenderResult } from "@testing-library/react";
|
||||
import { computed } from "mobx";
|
||||
import { noop } from "../../../../utils";
|
||||
import type { ApplicationBuilder } from "../../../test-utils/get-application-builder";
|
||||
import { getApplicationBuilder } from "../../../test-utils/get-application-builder";
|
||||
import type { PreferenceNavigationItem } from "../preference-navigation-items.injectable";
|
||||
import preferenceNavigationItemsInjectable from "../preference-navigation-items.injectable";
|
||||
|
||||
describe.only("preferences - navigation block with links", () => {
|
||||
let builder: ApplicationBuilder;
|
||||
|
||||
beforeEach(() => {
|
||||
builder = getApplicationBuilder();
|
||||
});
|
||||
|
||||
describe("given in preferences, when rendered", () => {
|
||||
let renderer: RenderResult;
|
||||
|
||||
describe("when general navigation items passed", () => {
|
||||
beforeEach(async () => {
|
||||
builder.beforeWindowStart((windowDi) => {
|
||||
windowDi.override(preferenceNavigationItemsInjectable, () =>
|
||||
computed(() => generalNavItems),
|
||||
);
|
||||
|
||||
builder.preferences.navigate();
|
||||
});
|
||||
|
||||
renderer = await builder.render();
|
||||
});
|
||||
|
||||
const links = ["General", "Proxy"];
|
||||
|
||||
it.each(links)("renders link with text content %s", (link) => {
|
||||
expect(renderer.container).toHaveTextContent(link);
|
||||
});
|
||||
|
||||
it("does not show custom settings block", () => {
|
||||
expect(
|
||||
renderer.queryByTestId("extension-settings"),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when general + extension navigation items passed", () => {
|
||||
beforeEach(async () => {
|
||||
builder.beforeWindowStart((windowDi) => {
|
||||
windowDi.override(preferenceNavigationItemsInjectable, () =>
|
||||
computed(() => [...generalNavItems, ...extensionNavItems]),
|
||||
);
|
||||
|
||||
builder.preferences.navigate();
|
||||
});
|
||||
|
||||
renderer = await builder.render();
|
||||
});
|
||||
|
||||
const generalLinks = ["General", "Proxy"];
|
||||
|
||||
it.each(generalLinks)(
|
||||
"renders general link with text content %s",
|
||||
(link) => {
|
||||
expect(renderer.container).toHaveTextContent(link);
|
||||
},
|
||||
);
|
||||
|
||||
it("shows custom settings block", () => {
|
||||
expect(
|
||||
renderer.queryByTestId("extension-settings"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const extensionLinks = ["lensapp-node-menu", "lensapp-pod-menu"];
|
||||
|
||||
it.each(extensionLinks)("shows extension navigation item %s", (link) => {
|
||||
expect(
|
||||
renderer.getByTestId(
|
||||
`tab-link-for-extension-preferences-navigation-item-${link}`,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders extension navigation items inside custom settings block", () => {
|
||||
const settingsBlock = renderer.getByTestId("extension-settings");
|
||||
|
||||
expect(settingsBlock).toHaveTextContent("lensapp-node-menu");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const generalNavItems: PreferenceNavigationItem[] = [
|
||||
{
|
||||
id: "general",
|
||||
label: "General",
|
||||
isActive: computed(() => false),
|
||||
isVisible: computed(() => true),
|
||||
navigate: () => noop,
|
||||
orderNumber: 0,
|
||||
parent: "general",
|
||||
},
|
||||
{
|
||||
id: "proxy",
|
||||
label: "Proxy",
|
||||
isActive: computed(() => false),
|
||||
isVisible: computed(() => true),
|
||||
navigate: () => noop,
|
||||
orderNumber: 1,
|
||||
parent: "general",
|
||||
},
|
||||
];
|
||||
|
||||
const extensionNavItems = [
|
||||
{
|
||||
id: "extension-preferences-navigation-item-lensapp-node-menu",
|
||||
label: "lensapp-node-menu",
|
||||
isActive: computed(() => false),
|
||||
isVisible: computed(() => true),
|
||||
navigate: () => noop,
|
||||
orderNumber: 0,
|
||||
parent: "extensions",
|
||||
},
|
||||
{
|
||||
id: "extension-preferences-navigation-item-lensapp-pod-menu",
|
||||
label: "lensapp-pod-menu",
|
||||
isActive: computed(() => false),
|
||||
isVisible: computed(() => true),
|
||||
navigate: () => noop,
|
||||
orderNumber: 0,
|
||||
parent: "extensions",
|
||||
},
|
||||
{
|
||||
id: "extension-preferences-navigation-item-metrics-plugin",
|
||||
label: "metrics-plugin",
|
||||
isActive: computed(() => false),
|
||||
isVisible: computed(() => false),
|
||||
navigate: () => noop,
|
||||
orderNumber: 0,
|
||||
parent: "extensions",
|
||||
},
|
||||
];
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import appPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/app/app-preferences-route.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const applicationPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "application-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(appPreferencesRouteInjectable);
|
||||
const navigateToPreferenceTab = di.inject(navigateToPreferenceTabInjectable);
|
||||
|
||||
const routeIsActive = di.inject(
|
||||
routeIsActiveInjectable,
|
||||
route,
|
||||
);
|
||||
|
||||
return {
|
||||
id: "application",
|
||||
label: "App",
|
||||
parent: "general",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
isVisible: computed(() => true),
|
||||
orderNumber: 10,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default applicationPreferencesNavigationItemInjectable;
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import editorPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/editor/editor-preferences-route.injectable";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const editorPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "editor-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(editorPreferencesRouteInjectable);
|
||||
const navigateToPreferenceTab = di.inject(navigateToPreferenceTabInjectable);
|
||||
|
||||
const routeIsActive = di.inject(
|
||||
routeIsActiveInjectable,
|
||||
route,
|
||||
);
|
||||
|
||||
return {
|
||||
id: "editor",
|
||||
label: "Editor",
|
||||
parent: "general",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
isVisible: computed(() => true),
|
||||
orderNumber: 40,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default editorPreferencesNavigationItemInjectable;
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import { map } from "lodash/fp";
|
||||
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
|
||||
import type { LensRendererExtension } from "../../../../extensions/lens-renderer-extension";
|
||||
import { extensionRegistratorInjectionToken } from "../../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import extensionPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
|
||||
import navigateToExtensionPreferencesInjectable from "../../../../common/front-end-routing/routes/preferences/extension/navigate-to-extension-preferences.injectable";
|
||||
import type { LensExtension } from "../../../../extensions/lens-extension";
|
||||
import routePathParametersInjectable from "../../../routes/route-path-parameters.injectable";
|
||||
|
||||
const extensionSpecificTabNavigationItemRegistratorInjectable = getInjectable({
|
||||
id: "extension-specific-tab-preferences-navigation-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
return (ext: LensExtension) => {
|
||||
const extension = ext as LensRendererExtension;
|
||||
const navigateToExtensionPreferences = di.inject(
|
||||
navigateToExtensionPreferencesInjectable,
|
||||
);
|
||||
const route = di.inject(extensionPreferencesRouteInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
const pathParameters = di.inject(routePathParametersInjectable, route);
|
||||
|
||||
return pipeline(
|
||||
extension.appPreferenceTabs,
|
||||
|
||||
map((tab) => {
|
||||
const id = `extension-${extension.sanitizedExtensionId}-nav-item-${tab.id}`;
|
||||
const isActive = computed(() => routeIsActive.get() && pathParameters.get().tabId === tab.id);
|
||||
|
||||
return getInjectable({
|
||||
id,
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
instantiate: () => ({
|
||||
id,
|
||||
label: tab.title,
|
||||
parent: "general",
|
||||
orderNumber: tab.orderNumber || 100,
|
||||
navigate: () => navigateToExtensionPreferences(extension.sanitizedExtensionId, tab.id),
|
||||
|
||||
isVisible: computed(() => {
|
||||
if (!tab.visible) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return tab.visible.get();
|
||||
}),
|
||||
|
||||
isActive,
|
||||
}),
|
||||
});
|
||||
}),
|
||||
);
|
||||
};
|
||||
},
|
||||
injectionToken: extensionRegistratorInjectionToken,
|
||||
});
|
||||
|
||||
export default extensionSpecificTabNavigationItemRegistratorInjectable;
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import type { PreferenceNavigationItem } from "./preference-navigation-items.injectable";
|
||||
import { Icon } from "../../icon";
|
||||
import { PreferencesNavigationTab } from "./preference-navigation-tab";
|
||||
import preferenceNavigationItemsForGroupInjectable from "./preference-navigation-items-for-group.injectable";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
interface Dependencies {
|
||||
navigationItems: IComputedValue<PreferenceNavigationItem[]>;
|
||||
}
|
||||
|
||||
const NonInjectedExtensionsNavGroup = observer((props: Dependencies) => {
|
||||
if (!props.navigationItems.get().length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-testid="extension-settings">
|
||||
<hr/>
|
||||
<div className="header flex items-center">
|
||||
<Icon
|
||||
material="extension"
|
||||
smallest
|
||||
className="mr-3"
|
||||
/>
|
||||
{" "}
|
||||
Extensions
|
||||
</div>
|
||||
<div>
|
||||
{props.navigationItems.get().map(item => (
|
||||
<PreferencesNavigationTab
|
||||
key={item.id}
|
||||
item={item}
|
||||
data-testid={`tab-link-for-${item.id}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const ExtensionsNavGroup = withInjectables<Dependencies>(
|
||||
NonInjectedExtensionsNavGroup,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
navigationItems: di.inject(preferenceNavigationItemsForGroupInjectable, "extensions"),
|
||||
}),
|
||||
},
|
||||
);
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import extensionPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
|
||||
import navigateToExtensionPreferencesInjectable from "../../../../common/front-end-routing/routes/preferences/extension/navigate-to-extension-preferences.injectable";
|
||||
import { extensionRegistratorInjectionToken } from "../../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import type { LensRendererExtension } from "../../../../extensions/lens-renderer-extension";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import routePathParametersInjectable from "../../../routes/route-path-parameters.injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
|
||||
const extensionPreferencesNavigationItemRegistratorInjectable = getInjectable({
|
||||
id: "extension-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
return (ext) => {
|
||||
const extension = ext as LensRendererExtension;
|
||||
const navigateToExtensionPreferences = di.inject(
|
||||
navigateToExtensionPreferencesInjectable,
|
||||
);
|
||||
|
||||
const extensionHasPreferences = extension.appPreferences.length > 0;
|
||||
const extensionHasGeneralPreferences = extension.appPreferences.some(preferences =>
|
||||
!preferences.showInPreferencesTab,
|
||||
);
|
||||
const isVisible = computed(() => extensionHasPreferences && extensionHasGeneralPreferences);
|
||||
const extensionRoute = di.inject(extensionPreferencesRouteInjectable);
|
||||
const pathParameters = di.inject(routePathParametersInjectable, extensionRoute);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, extensionRoute);
|
||||
const isActive = computed(() => routeIsActive.get() && pathParameters.get().extensionId === extension.sanitizedExtensionId);
|
||||
const id = `extension-preferences-navigation-item-${extension.sanitizedExtensionId}`;
|
||||
|
||||
const injectable = getInjectable({
|
||||
id,
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
instantiate: () => ({
|
||||
id: `extension-${extension.sanitizedExtensionId}`,
|
||||
label: `${extension.name}`,
|
||||
navigate: () => navigateToExtensionPreferences(extension.sanitizedExtensionId),
|
||||
isActive,
|
||||
isVisible,
|
||||
orderNumber: 20,
|
||||
parent: "extensions",
|
||||
}),
|
||||
});
|
||||
|
||||
return [injectable];
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: extensionRegistratorInjectionToken,
|
||||
});
|
||||
|
||||
export default extensionPreferencesNavigationItemRegistratorInjectable;
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import type { PreferenceNavigationItem } from "./preference-navigation-items.injectable";
|
||||
import { PreferencesNavigationTab } from "./preference-navigation-tab";
|
||||
import preferenceNavigationItemsForGroupInjectable from "./preference-navigation-items-for-group.injectable";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
interface Dependencies {
|
||||
navigationItems: IComputedValue<PreferenceNavigationItem[]>;
|
||||
}
|
||||
|
||||
const NonInjectedGeneralNavGroup = observer((props: Dependencies) => {
|
||||
if (!props.navigationItems.get().length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="header">Preferences</div>
|
||||
|
||||
{props.navigationItems.get().map(item => (
|
||||
<PreferencesNavigationTab
|
||||
key={item.id}
|
||||
item={item}
|
||||
data-testid={`tab-link-for-${item.id}`}
|
||||
/>
|
||||
))}
|
||||
</React.Fragment>
|
||||
);
|
||||
});
|
||||
|
||||
export const GeneralNavGroup = withInjectables<Dependencies>(
|
||||
NonInjectedGeneralNavGroup,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
navigationItems: di.inject(preferenceNavigationItemsForGroupInjectable, "general"),
|
||||
}),
|
||||
},
|
||||
);
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import kubernetesPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/kubernetes/kubernetes-preferences-route.injectable";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const kubernetesPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "kubernetes-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const navigateToPreferenceTab = di.inject(navigateToPreferenceTabInjectable);
|
||||
|
||||
const route = di.inject(
|
||||
kubernetesPreferencesRouteInjectable,
|
||||
);
|
||||
|
||||
const routeIsActive = di.inject(
|
||||
routeIsActiveInjectable,
|
||||
route,
|
||||
);
|
||||
|
||||
return {
|
||||
id: "kubernetes",
|
||||
label: "Kubernetes",
|
||||
parent: "general",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
isVisible: computed(() => true),
|
||||
orderNumber: 30,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default kubernetesPreferencesNavigationItemInjectable;
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { Route } from "../../../../common/front-end-routing/front-end-route-injection-token";
|
||||
import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token";
|
||||
|
||||
const navigateToPreferenceTabInjectable = getInjectable({
|
||||
id: "navigate-to-preference-tab",
|
||||
|
||||
instantiate: (di) => {
|
||||
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
|
||||
|
||||
return (route: Route) => () => {
|
||||
navigateToRoute(route, { withoutAffectingBackButton: true });
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default navigateToPreferenceTabInjectable;
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import {
|
||||
getInjectable,
|
||||
lifecycleEnum,
|
||||
} from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import type { PreferenceNavigationItem } from "./preference-navigation-items.injectable";
|
||||
import preferenceNavigationItemsInjectable from "./preference-navigation-items.injectable";
|
||||
|
||||
const preferenceNavigationItemsForGroupInjectable = getInjectable({
|
||||
id: "preference-navigation-items-for-group",
|
||||
|
||||
instantiate: (di, group: string) => {
|
||||
const preferenceNavigationItems = di.inject(preferenceNavigationItemsInjectable);
|
||||
|
||||
return computed((): PreferenceNavigationItem[] =>
|
||||
preferenceNavigationItems.get().filter((item) => item.parent == group),
|
||||
);
|
||||
},
|
||||
|
||||
lifecycle: lifecycleEnum.keyedSingleton({
|
||||
getInstanceKey: (di, group: string) => group,
|
||||
}),
|
||||
});
|
||||
|
||||
export default preferenceNavigationItemsForGroupInjectable;
|
||||
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
|
||||
import { filter, orderBy } from "lodash/fp";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { computed } from "mobx";
|
||||
|
||||
export const preferenceNavigationItemInjectionToken =
|
||||
getInjectionToken<PreferenceNavigationItem>({
|
||||
id: "preference-navigation-item-injection-token",
|
||||
});
|
||||
|
||||
export interface PreferenceNavigationItem {
|
||||
id: string;
|
||||
label: string;
|
||||
isActive: IComputedValue<boolean>;
|
||||
isVisible: IComputedValue<boolean>;
|
||||
navigate: () => void;
|
||||
orderNumber: number;
|
||||
parent: string;
|
||||
}
|
||||
|
||||
const preferenceNavigationItemsInjectable = getInjectable({
|
||||
id: "preference-navigation-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const computedInjectMany = di.inject(computedInjectManyInjectable);
|
||||
const navigationItems = computedInjectMany(preferenceNavigationItemInjectionToken);
|
||||
|
||||
return computed((): PreferenceNavigationItem[] =>
|
||||
pipeline(
|
||||
navigationItems.get(),
|
||||
filter((item) => !!item.isVisible.get()),
|
||||
(items) => orderBy([(item) => item.orderNumber], ["asc"], items),
|
||||
),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default preferenceNavigationItemsInjectable;
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { Tab } from "../../tabs";
|
||||
import type { PreferenceNavigationItem } from "./preference-navigation-items.injectable";
|
||||
|
||||
interface PreferenceNavigationTabProps extends React.DOMAttributes<HTMLElement> {
|
||||
item: PreferenceNavigationItem;
|
||||
}
|
||||
|
||||
export const PreferencesNavigationTab = observer(({ item }: PreferenceNavigationTabProps) => (
|
||||
<Tab
|
||||
value={item}
|
||||
label={item.label}
|
||||
data-testid={`tab-link-for-${item.id}`}
|
||||
active={item.isActive.get()}
|
||||
/>
|
||||
));
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { Tabs } from "../../tabs";
|
||||
import { ExtensionsNavGroup } from "./extensions-nav-group";
|
||||
import { GeneralNavGroup } from "./general-nav-group";
|
||||
import type {
|
||||
PreferenceNavigationItem,
|
||||
} from "./preference-navigation-items.injectable";
|
||||
|
||||
export const PreferencesNavigation = () => {
|
||||
return (
|
||||
<Tabs
|
||||
className="flex column"
|
||||
scrollable={false}
|
||||
onChange={(item: PreferenceNavigationItem) => item.navigate()}
|
||||
>
|
||||
<GeneralNavGroup/>
|
||||
<ExtensionsNavGroup/>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import proxyPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/proxy/proxy-preferences-route.injectable";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const proxyPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "proxy-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(proxyPreferencesRouteInjectable);
|
||||
|
||||
const navigateToPreferenceTab = di.inject(
|
||||
navigateToPreferenceTabInjectable,
|
||||
);
|
||||
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return {
|
||||
id: "proxy",
|
||||
label: "Proxy",
|
||||
parent: "general",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
isVisible: computed(() => true),
|
||||
orderNumber: 20,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default proxyPreferencesNavigationItemInjectable;
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import telemetryPreferenceItemsInjectable from "../telemetry-preference-items.injectable";
|
||||
import telemetryPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/telemetry/telemetry-preferences-route.injectable";
|
||||
import sentryDataSourceNameInjectable from "../../../../common/vars/sentry-dsn-url.injectable";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const terminalPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "telemetry-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const sentryDataSourceName = di.inject(sentryDataSourceNameInjectable);
|
||||
|
||||
const telemetryPreferenceItems = di.inject(
|
||||
telemetryPreferenceItemsInjectable,
|
||||
);
|
||||
|
||||
const navigateToPreferenceTab = di.inject(
|
||||
navigateToPreferenceTabInjectable,
|
||||
);
|
||||
|
||||
const route = di.inject(telemetryPreferencesRouteInjectable);
|
||||
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return {
|
||||
id: "telemetry",
|
||||
label: "Telemetry",
|
||||
parent: "general",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
|
||||
isVisible: computed(
|
||||
() => !!sentryDataSourceName || telemetryPreferenceItems.get().length > 0,
|
||||
),
|
||||
|
||||
orderNumber: 60,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default terminalPreferencesNavigationItemInjectable;
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import terminalPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/terminal/terminal-preferences-route.injectable";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const terminalPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "terminal-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const navigateToPreferenceTab = di.inject(
|
||||
navigateToPreferenceTabInjectable,
|
||||
);
|
||||
|
||||
const route = di.inject(terminalPreferencesRouteInjectable);
|
||||
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return {
|
||||
id: "terminal",
|
||||
label: "Terminal",
|
||||
parent: "general",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
isVisible: computed(() => true),
|
||||
orderNumber: 50,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default terminalPreferencesNavigationItemInjectable;
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
.Preferences {
|
||||
.loading-spinner {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import "./preferences.scss";
|
||||
import React from "react";
|
||||
|
||||
import { SettingLayout } from "../layout/setting-layout";
|
||||
import { PreferencesNavigation } from "./preferences-navigation/preferences-navigation";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import closePreferencesInjectable from "./close-preferences.injectable";
|
||||
|
||||
interface PreferencesProps extends React.DOMAttributes<any> {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
closePreferences: () => void;
|
||||
}
|
||||
|
||||
const NonInjectedPreferences = ({
|
||||
children,
|
||||
closePreferences,
|
||||
...props
|
||||
}: PreferencesProps & Dependencies) => (
|
||||
<SettingLayout
|
||||
navigation={<PreferencesNavigation />}
|
||||
className="Preferences"
|
||||
contentGaps={false}
|
||||
closeButtonProps={{ "data-testid": "close-preferences" }}
|
||||
back={closePreferences}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</SettingLayout>
|
||||
);
|
||||
|
||||
export const Preferences = withInjectables<Dependencies, PreferencesProps>(
|
||||
NonInjectedPreferences,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
closePreferences: di.inject(closePreferencesInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import proxyPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/proxy/proxy-preferences-route.injectable";
|
||||
import { LensProxy } from "./proxy";
|
||||
|
||||
const proxyPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "proxy-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(proxyPreferencesRouteInjectable),
|
||||
Component: LensProxy,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default proxyPreferencesRouteComponentInjectable;
|
||||
@@ -1,71 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { Input } from "../input";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { Switch } from "../switch";
|
||||
import { Preferences } from "./preferences";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
userStore: UserStore;
|
||||
}
|
||||
|
||||
const NonInjectedLensProxy = observer(({ userStore }: Dependencies) => {
|
||||
const [proxy, setProxy] = React.useState(userStore.httpsProxy || "");
|
||||
|
||||
return (
|
||||
<Preferences data-testid="proxy-preferences-page">
|
||||
<section id="proxy">
|
||||
<section>
|
||||
<h2 data-testid="proxy-header">Proxy</h2>
|
||||
<SubTitle title="HTTP Proxy" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
placeholder="Type HTTP proxy url (example: http://proxy.acme.org:8080)"
|
||||
value={proxy}
|
||||
onChange={v => setProxy(v)}
|
||||
onBlur={() => userStore.httpsProxy = proxy}
|
||||
/>
|
||||
<small className="hint">
|
||||
Proxy is used only for non-cluster communication.
|
||||
</small>
|
||||
</section>
|
||||
|
||||
<hr className="small" />
|
||||
|
||||
<section className="small">
|
||||
<SubTitle title="Certificate Trust" />
|
||||
<Switch
|
||||
checked={userStore.allowUntrustedCAs}
|
||||
onChange={() => userStore.allowUntrustedCAs = !userStore.allowUntrustedCAs}
|
||||
>
|
||||
Allow untrusted Certificate Authorities
|
||||
</Switch>
|
||||
<small className="hint">
|
||||
This will make Lens to trust ANY certificate authority without any validations.
|
||||
{" "}
|
||||
Needed with some corporate proxies that do certificate re-writing.
|
||||
{" "}
|
||||
Does not affect cluster communications!
|
||||
</small>
|
||||
</section>
|
||||
</section>
|
||||
</Preferences>
|
||||
);
|
||||
});
|
||||
|
||||
export const LensProxy = withInjectables<Dependencies>(
|
||||
NonInjectedLensProxy,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -1,8 +0,0 @@
|
||||
.item {
|
||||
--flex-gap: 0.5rem;
|
||||
|
||||
background: var(--inputControlBackground);
|
||||
min-height: 65px;
|
||||
border-radius: 3px;
|
||||
padding: calc(var(--padding) * 1.5);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import styles from "./removable-item.module.scss";
|
||||
|
||||
import type { DOMAttributes } from "react";
|
||||
import React from "react";
|
||||
|
||||
import { cssNames } from "../../utils";
|
||||
import { Icon } from "../icon";
|
||||
|
||||
export interface RemovableItemProps extends DOMAttributes<any>{
|
||||
icon?: string;
|
||||
onRemove: () => void;
|
||||
className?: string;
|
||||
"data-testid"?: string;
|
||||
}
|
||||
|
||||
export function RemovableItem({ icon, onRemove, children, className, "data-testid": testId, ...rest }: RemovableItemProps) {
|
||||
return (
|
||||
<div className={cssNames(styles.item, "flex gaps align-center justify-space-between", className)} {...rest}>
|
||||
{icon && (
|
||||
<Icon material={icon}/>
|
||||
)}
|
||||
{children}
|
||||
<Icon
|
||||
material="delete"
|
||||
onClick={onRemove}
|
||||
tooltip="Remove"
|
||||
data-testid={testId}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
|
||||
import type { AppPreferenceRegistration } from "./app-preferences/app-preference-registration";
|
||||
|
||||
export const telemetryPreferenceItemInjectionToken = getInjectionToken<AppPreferenceRegistration>({
|
||||
id: "telemetry-preference-item-injection-token",
|
||||
});
|
||||
|
||||
const telemetryPreferenceItemsInjectable = getInjectable({
|
||||
id: "telemetry-preference-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const computedInjectMany = di.inject(computedInjectManyInjectable);
|
||||
|
||||
return computedInjectMany(telemetryPreferenceItemInjectionToken);
|
||||
},
|
||||
});
|
||||
|
||||
export default telemetryPreferenceItemsInjectable;
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import telemetryPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/telemetry/telemetry-preferences-route.injectable";
|
||||
import { Telemetry } from "./telemetry";
|
||||
|
||||
const telemetryPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "telemetry-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(telemetryPreferencesRouteInjectable),
|
||||
Component: Telemetry,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default telemetryPreferencesRouteComponentInjectable;
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { Checkbox } from "../checkbox";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { ExtensionSettings } from "./extension-settings";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { Preferences } from "./preferences";
|
||||
import telemetryPreferenceItemsInjectable from "./telemetry-preference-items.injectable";
|
||||
import sentryDataSourceNameInjectable from "../../../common/vars/sentry-dsn-url.injectable";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import type { AppPreferenceRegistration } from "./app-preferences/app-preference-registration";
|
||||
|
||||
interface Dependencies {
|
||||
telemetryPreferenceItems: IComputedValue<AppPreferenceRegistration[]>;
|
||||
sentryDnsUrl: string;
|
||||
userStore: UserStore;
|
||||
}
|
||||
|
||||
const NonInjectedTelemetry: React.FC<Dependencies> = ({
|
||||
telemetryPreferenceItems,
|
||||
sentryDnsUrl,
|
||||
userStore,
|
||||
}) => (
|
||||
<Preferences data-testid="telemetry-preferences-page">
|
||||
<section id="telemetry">
|
||||
<h2 data-testid="telemetry-header">Telemetry</h2>
|
||||
{telemetryPreferenceItems.get().map((item) => (
|
||||
<ExtensionSettings
|
||||
key={item.id}
|
||||
setting={item}
|
||||
size="small"
|
||||
data-testid={`telemetry-preference-item-for-${item.id}`}
|
||||
/>
|
||||
))}
|
||||
{sentryDnsUrl ? (
|
||||
<React.Fragment key="sentry">
|
||||
<section
|
||||
id="sentry"
|
||||
className="small"
|
||||
data-testid="telemetry-preferences-for-automatic-error-reporting"
|
||||
>
|
||||
<SubTitle title="Automatic Error Reporting" />
|
||||
<Checkbox
|
||||
label="Allow automatic error reporting"
|
||||
value={userStore.allowErrorReporting}
|
||||
onChange={(value) => userStore.allowErrorReporting = value}
|
||||
/>
|
||||
<div className="hint">
|
||||
<span>
|
||||
Automatic error reports provide vital information about issues
|
||||
and application crashes. It is highly recommended to keep this
|
||||
feature enabled to ensure fast turnaround for issues you might
|
||||
encounter.
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
<hr className="small" />
|
||||
</React.Fragment>
|
||||
) : // we don't need to shows the checkbox at all if Sentry dsn is not a valid url
|
||||
null}
|
||||
</section>
|
||||
</Preferences>
|
||||
);
|
||||
|
||||
export const Telemetry = withInjectables<Dependencies>(
|
||||
observer(NonInjectedTelemetry),
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
telemetryPreferenceItems: di.inject(telemetryPreferenceItemsInjectable),
|
||||
sentryDnsUrl: di.inject(sentryDataSourceNameInjectable),
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import terminalPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/terminal/terminal-preferences-route.injectable";
|
||||
import { Terminal } from "./terminal";
|
||||
|
||||
const terminalPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "terminal-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(terminalPreferencesRouteInjectable),
|
||||
Component: Terminal,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default terminalPreferencesRouteComponentInjectable;
|
||||
@@ -1,138 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { action } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { Input } from "../input";
|
||||
import { Switch } from "../switch";
|
||||
import { Select, type SelectOption } from "../select";
|
||||
import type { ThemeStore } from "../../themes/store";
|
||||
import { Preferences } from "./preferences";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import themeStoreInjectable from "../../themes/store.injectable";
|
||||
import defaultShellInjectable from "./default-shell.injectable";
|
||||
import logger from "../../../common/logger";
|
||||
|
||||
interface Dependencies {
|
||||
userStore: UserStore;
|
||||
themeStore: ThemeStore;
|
||||
defaultShell: string;
|
||||
}
|
||||
|
||||
const NonInjectedTerminal = observer((
|
||||
{
|
||||
userStore,
|
||||
themeStore,
|
||||
defaultShell,
|
||||
}: Dependencies) => {
|
||||
const themeOptions = [
|
||||
{
|
||||
value: "", // TODO: replace with a sentinal value that isn't string (and serialize it differently)
|
||||
label: "Match Lens Theme",
|
||||
},
|
||||
...Array.from(themeStore.themes, ([themeId, { name }]) => ({
|
||||
value: themeId,
|
||||
label: name,
|
||||
})),
|
||||
];
|
||||
|
||||
// fonts must be declared in `fonts.scss` and at `template.html` (if early-preloading required)
|
||||
const supportedCustomFonts: SelectOption<string>[] = [
|
||||
"RobotoMono", "Anonymous Pro", "IBM Plex Mono", "JetBrains Mono", "Red Hat Mono",
|
||||
"Source Code Pro", "Space Mono", "Ubuntu Mono",
|
||||
].map(customFont => {
|
||||
const { fontFamily, fontSize } = userStore.terminalConfig;
|
||||
|
||||
return {
|
||||
label: <span style={{ fontFamily: customFont, fontSize }}>{customFont}</span>,
|
||||
value: customFont,
|
||||
isSelected: fontFamily === customFont,
|
||||
};
|
||||
});
|
||||
|
||||
const onFontFamilyChange = action(({ value: fontFamily }: SelectOption<string>) => {
|
||||
logger.info(`setting terminal font to ${fontFamily}`);
|
||||
|
||||
userStore.terminalConfig.fontFamily = fontFamily; // save to external storage
|
||||
});
|
||||
|
||||
return (
|
||||
<Preferences data-testid="terminal-preferences-page">
|
||||
<section>
|
||||
<h2>Terminal</h2>
|
||||
|
||||
<section id="shell">
|
||||
<SubTitle title="Terminal Shell Path" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
placeholder={defaultShell}
|
||||
value={userStore.shell ?? ""}
|
||||
onChange={(value) => userStore.shell = value}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section id="terminalSelection">
|
||||
<SubTitle title="Terminal copy & paste" />
|
||||
<Switch
|
||||
checked={userStore.terminalCopyOnSelect}
|
||||
onChange={() => userStore.terminalCopyOnSelect = !userStore.terminalCopyOnSelect}
|
||||
>
|
||||
Copy on select and paste on right-click
|
||||
</Switch>
|
||||
</section>
|
||||
|
||||
<section id="terminalTheme">
|
||||
<SubTitle title="Terminal theme" />
|
||||
<Select
|
||||
id="terminal-theme-input"
|
||||
themeName="lens"
|
||||
options={themeOptions}
|
||||
value={userStore.terminalTheme}
|
||||
onChange={option => userStore.terminalTheme = option?.value ?? ""}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Font size" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="number"
|
||||
min={10}
|
||||
max={50}
|
||||
defaultValue={userStore.terminalConfig.fontSize.toString()}
|
||||
onChange={(value) => userStore.terminalConfig.fontSize = Number(value)}
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<SubTitle title="Font family" />
|
||||
<Select
|
||||
themeName="lens"
|
||||
controlShouldRenderValue
|
||||
value={userStore.terminalConfig.fontFamily}
|
||||
options={supportedCustomFonts}
|
||||
onChange={onFontFamilyChange as any}
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
</Preferences>
|
||||
);
|
||||
});
|
||||
|
||||
export const Terminal = withInjectables<Dependencies>(
|
||||
NonInjectedTerminal,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
themeStore: di.inject(themeStoreInjectable),
|
||||
defaultShell: di.inject(defaultShellInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
+3
-1
@@ -15,7 +15,6 @@ import { getInjectable } from "@ogre-tools/injectable";
|
||||
import commandOverlayInjectable from "../command-overlay.injectable";
|
||||
import createTerminalTabInjectable from "../../dock/terminal/create-terminal-tab.injectable";
|
||||
import type { DockTabCreate } from "../../dock/dock/store";
|
||||
import navigateToPreferencesInjectable from "../../../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable";
|
||||
import navigateToHelmChartsInjectable from "../../../../common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable";
|
||||
import navigateToHelmReleasesInjectable from "../../../../common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable";
|
||||
import navigateToConfigMapsInjectable from "../../../../common/front-end-routing/routes/cluster/config/config-maps/navigate-to-config-maps.injectable";
|
||||
@@ -40,6 +39,9 @@ import navigateToCustomResourcesInjectable from "../../../../common/front-end-ro
|
||||
import navigateToEntitySettingsInjectable from "../../../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable";
|
||||
import getEntitySettingCommandsInjectable from "./get-entity-setting-commands.injectable";
|
||||
|
||||
// TODO: Importing from features is not OK. Make commands to comply with Open Closed Principle to allow moving implementation under a feature
|
||||
import navigateToPreferencesInjectable from "../../../../features/preferences/common/navigate-to-preferences.injectable";
|
||||
|
||||
export function isKubernetesClusterActive(context: CommandContext): boolean {
|
||||
return context.entity?.kind === "KubernetesCluster";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
@mixin gutterSize(
|
||||
$sizeName,
|
||||
$size
|
||||
) {
|
||||
.size-#{$sizeName} {
|
||||
min-width: $size;
|
||||
min-height: $size;
|
||||
}
|
||||
}
|
||||
|
||||
$baseline: 8px;
|
||||
|
||||
@include gutterSize('sm', 2 * $baseline);
|
||||
@include gutterSize('md', 3 * $baseline);
|
||||
@include gutterSize('xl', 5 * $baseline);
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { cssNames } from "../../utils";
|
||||
import styles from "./gutter.module.scss";
|
||||
|
||||
interface GutterProps {
|
||||
size?: "sm" | "md" | "xl";
|
||||
}
|
||||
|
||||
const Gutter = ({ size = "md" }: GutterProps) => {
|
||||
const classNames = cssNames(styles[`size-${size}`]);
|
||||
|
||||
return <div className={classNames} />;
|
||||
};
|
||||
|
||||
|
||||
export default Gutter;
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
.HorizontalLine {
|
||||
height: 1px;
|
||||
border-top: thin solid var(--hrColor);
|
||||
}
|
||||
|
||||
@mixin horizontalLineSize(
|
||||
$sizeName,
|
||||
$size
|
||||
) {
|
||||
.size-#{$sizeName} {
|
||||
margin-top: $size;
|
||||
margin-bottom: $size;
|
||||
}
|
||||
}
|
||||
|
||||
$baseline: 8px;
|
||||
|
||||
@include horizontalLineSize('sm', 2 * $baseline);
|
||||
@include horizontalLineSize('md', 3 * $baseline);
|
||||
@include horizontalLineSize('xl', 5 * $baseline);
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import styles from "./horizontal-line.module.scss";
|
||||
import { cssNames } from "../../utils";
|
||||
|
||||
interface HorizontalLineProps {
|
||||
size?: "sm" | "md" | "xl";
|
||||
}
|
||||
|
||||
export const HorizontalLine = ({ size = "xl" }: HorizontalLineProps = { size: "xl" }) => {
|
||||
const classNames = cssNames(styles.HorizontalLine, styles[`size-${size}`]);
|
||||
|
||||
return <div className={classNames} />;
|
||||
};
|
||||
@@ -121,14 +121,6 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-top: 40px;
|
||||
|
||||
&.small {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
color: var(--textColorAccent);
|
||||
}
|
||||
@@ -167,15 +159,14 @@
|
||||
|
||||
hr {
|
||||
margin-top: 40px;
|
||||
margin-bottom: 40px;
|
||||
|
||||
height: 1px;
|
||||
border-top: thin solid var(--hrColor);
|
||||
|
||||
&.small {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
display: none;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import toggleMaximizeWindowInjectable from "./toggle-maximize-window.injectable";
|
||||
import { getGlobalOverrideForFunction } from "../../../../../common/test-utils/get-global-override-for-function";
|
||||
|
||||
export default getGlobalOverrideForFunction(toggleMaximizeWindowInjectable);
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { WindowAction } from "../../../../common/ipc/window";
|
||||
import { requestWindowAction } from "../../../ipc";
|
||||
import { WindowAction } from "../../../../../common/ipc/window";
|
||||
import { requestWindowAction } from "../../../../ipc";
|
||||
|
||||
const toggleMaximizeWindowInjectable = getInjectable({
|
||||
id: "toggle-maximize-window",
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import isLinuxInjectable from "../../../../../../common/vars/is-linux.injectable";
|
||||
import isWindowsInjectable from "../../../../../../common/vars/is-windows.injectable";
|
||||
import { computed } from "mobx";
|
||||
import { ContextMenu } from "./context-menu";
|
||||
import { topBarItemOnLeftSideInjectionToken } from "../top-bar-item-injection-token";
|
||||
|
||||
const contextMenuTopBarItemInjectable = getInjectable({
|
||||
id: "context-menu-top-bar-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const isWindows = di.inject(isWindowsInjectable);
|
||||
const isLinux = di.inject(isLinuxInjectable);
|
||||
|
||||
return {
|
||||
id: "context-menu",
|
||||
isShown: computed(() => isWindows || isLinux),
|
||||
orderNumber: 10,
|
||||
Component: ContextMenu,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: topBarItemOnLeftSideInjectionToken,
|
||||
});
|
||||
|
||||
export default contextMenuTopBarItemInjectable;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import React from "react";
|
||||
import styles from "../../top-bar.module.scss";
|
||||
import openAppContextMenuInjectable from "./open-app-context-menu/open-app-context-menu.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
openAppContextMenu: () => void;
|
||||
}
|
||||
|
||||
const NonInjectedContextMenu = ({ openAppContextMenu }: Dependencies) => (
|
||||
<div className={styles.winMenu}>
|
||||
<div onClick={openAppContextMenu} data-testid="window-menu">
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
shapeRendering="crispEdges"
|
||||
>
|
||||
<path fill="currentColor" d="M0,8.5h12v1H0V8.5z" />
|
||||
<path fill="currentColor" d="M0,5.5h12v1H0V5.5z" />
|
||||
<path fill="currentColor" d="M0,2.5h12v1H0V2.5z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const ContextMenu = withInjectables<Dependencies>(
|
||||
NonInjectedContextMenu,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
openAppContextMenu: di.inject(openAppContextMenuInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import openAppContextMenuInjectable from "./open-app-context-menu.injectable";
|
||||
import { getGlobalOverrideForFunction } from "../../../../../../../common/test-utils/get-global-override-for-function";
|
||||
|
||||
export default getGlobalOverrideForFunction(openAppContextMenuInjectable);
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { emitOpenAppMenuAsContextMenu } from "../../../ipc";
|
||||
import { emitOpenAppMenuAsContextMenu } from "../../../../../../ipc";
|
||||
|
||||
const openAppContextMenuInjectable = getInjectable({
|
||||
id: "open-app-context-menu",
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import goBackInjectable from "./go-back.injectable";
|
||||
import { getGlobalOverrideForFunction } from "../../../../../../../common/test-utils/get-global-override-for-function";
|
||||
|
||||
export default getGlobalOverrideForFunction(goBackInjectable);
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { WindowAction } from "../../../../common/ipc/window";
|
||||
import { requestWindowAction } from "../../../ipc";
|
||||
import { WindowAction } from "../../../../../../../common/ipc/window";
|
||||
import { requestWindowAction } from "../../../../../../ipc";
|
||||
|
||||
const goBackInjectable = getInjectable({
|
||||
id: "go-back",
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import { topBarItemOnLeftSideInjectionToken } from "../top-bar-item-injection-token";
|
||||
import { NavigationToBack } from "./navigation-to-back";
|
||||
|
||||
const navigationToBackTopBarItemInjectable = getInjectable({
|
||||
id: "navigation-to-back-top-bar-item",
|
||||
|
||||
instantiate: () => ({
|
||||
id: "navigation-to-back",
|
||||
isShown: computed(() => true),
|
||||
orderNumber: 30,
|
||||
Component: NavigationToBack,
|
||||
}),
|
||||
|
||||
injectionToken: topBarItemOnLeftSideInjectionToken,
|
||||
});
|
||||
|
||||
export default navigationToBackTopBarItemInjectable;
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { Icon } from "../../../../icon";
|
||||
import topBarPrevEnabledInjectable from "./prev-enabled.injectable";
|
||||
import goBackInjectable from "./go-back/go-back.injectable";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
interface Dependencies {
|
||||
prevEnabled: IComputedValue<boolean>;
|
||||
goBack: () => void;
|
||||
}
|
||||
|
||||
const NonInjectedNavigationToBack = observer(({
|
||||
prevEnabled,
|
||||
goBack,
|
||||
}: Dependencies) => (
|
||||
<Icon
|
||||
data-testid="history-back"
|
||||
material="arrow_back"
|
||||
onClick={goBack}
|
||||
disabled={!prevEnabled.get()}
|
||||
/>
|
||||
));
|
||||
|
||||
export const NavigationToBack = withInjectables<Dependencies>(
|
||||
NonInjectedNavigationToBack,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
prevEnabled: di.inject(topBarPrevEnabledInjectable),
|
||||
goBack: di.inject(goBackInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import topBarStateInjectable from "./state.injectable";
|
||||
import topBarStateInjectable from "../../state.injectable";
|
||||
|
||||
const topBarPrevEnabledInjectable = getInjectable({
|
||||
id: "top-bar-prev-enabled",
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import goForwardInjectable from "./go-forward.injectable";
|
||||
import { getGlobalOverrideForFunction } from "../../../../../../../common/test-utils/get-global-override-for-function";
|
||||
|
||||
export default getGlobalOverrideForFunction(goForwardInjectable);
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { WindowAction } from "../../../../common/ipc/window";
|
||||
import { requestWindowAction } from "../../../ipc";
|
||||
import { WindowAction } from "../../../../../../../common/ipc/window";
|
||||
import { requestWindowAction } from "../../../../../../ipc";
|
||||
|
||||
const goForwardInjectable = getInjectable({
|
||||
id: "go-forward",
|
||||
Loaded 100 of 418 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user