Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Improve quasarConfOptions, generate types for it, improve docs (fix: #14069) #15945

Conversation

yusufkandemir
Copy link
Member

What kind of change does this PR introduce?

  • Feature
  • Documentation
  • Refactor

Does this PR introduce a breaking change?

  • No

The PR fulfills these requirements:

  • It's submitted to the dev branch (or v[X] branch)
  • When resolving a specific issue, it's referenced in the PR's title (e.g. fix: #xxx[,#xxx], where "xxx" is the issue number)

Other information:
Resolves #14069.

Previously undocumented non-object configuration example(Dark plugin):
image
Previously undocumented "plugin-like" examples:
image
image

Config-only installation cards use the title "Configuration" for improved clarity.

We are now generating and exporting a new type called QuasarUIConfiguration from quasar that holds the configurable options. That type will now be used by the config key of the Vue plugin installation, so Vite plugin/Vue CLI users will get config type-safety now. app-vite and app-webpack will use this type for quasar.config file types, so the types will always be in-sync without effort, and be compatible with the UI package version used in the app.

@github-actions
Copy link

github-actions bot commented Jun 5, 2023

Build Results

JSON API

📜 Changes detected:

diff --git a/./pr-build/api/Brand.json b/./pr-build/api/Brand.json
new file mode 100644
index 0000000..9f5ee1c
--- /dev/null
+++ b/./pr-build/api/Brand.json
@@ -0,0 +1,49 @@
+{
+  "type": "plugin",
+  "meta": {
+    "docsUrl": "https://v2.quasar.dev/style/color-palette"
+  },
+  "quasarConfOptions": {
+    "propName": "brand",
+    "type": "Object",
+    "definition": {
+      "primary": {
+        "type": "String",
+        "desc": "Main color of your app"
+      },
+      "secondary": {
+        "type": "String",
+        "desc": "Secondary color of your app"
+      },
+      "accent": {
+        "type": "String",
+        "desc": "Accent color of your app"
+      },
+      "dark": {
+        "type": "String",
+        "desc": "Dark color of your app"
+      },
+      "positive": {
+        "type": "String",
+        "desc": "Positive color of your app"
+      },
+      "negative": {
+        "type": "String",
+        "desc": "Negative color of your app"
+      },
+      "info": {
+        "type": "String",
+        "desc": "Info color of your app"
+      },
+      "warning": {
+        "type": "String",
+        "desc": "Warning color of your app"
+      },
+      "...customColors": {
+        "type": "String",
+        "desc": "Custom colors of your app, if any"
+      }
+    }
+  },
+  "internal": true
+}
\ No newline at end of file
diff --git a/./current-build/api/Dark.json b/./pr-build/api/Dark.json
index 328e403..274346a 100644
--- a/./current-build/api/Dark.json
+++ b/./pr-build/api/Dark.json
@@ -4,6 +4,19 @@
     "docsUrl": "https://v2.quasar.dev/quasar-plugins/dark"
   },
   "injection": "$q.dark",
+  "quasarConfOptions": {
+    "propName": "dark",
+    "type": [
+      "Boolean",
+      "String"
+    ],
+    "desc": "\"'auto'\" uses the OS/browser preference. \"true\" forces dark mode. \"false\" forces light mode.",
+    "values": [
+      "auto",
+      "(Boolean) true",
+      "(Boolean) false"
+    ]
+  },
   "props": {
     "isActive": {
       "type": "Boolean",
diff --git a/./pr-build/api/Lang.json b/./pr-build/api/Lang.json
new file mode 100644
index 0000000..bffce35
--- /dev/null
+++ b/./pr-build/api/Lang.json
@@ -0,0 +1,18 @@
+{
+  "type": "plugin",
+  "meta": {
+    "docsUrl": "https://v2.quasar.dev/options/quasar-language-packs"
+  },
+  "quasarConfOptions": {
+    "propName": "lang",
+    "type": "Object",
+    "definition": {
+      "noHtmlAttrs": {
+        "type": "Boolean",
+        "addedIn": "v2.11.3",
+        "desc": "Whether to disable `dir` and `lang` HTML attributes getting added to the `<html>` tag. `dir` attribute is crucial when using RTL support. Disable this only if you need to handle these yourself for some reason."
+      }
+    }
+  },
+  "internal": true
+}
\ No newline at end of file
diff --git a/./current-build/api/Loading.json b/./pr-build/api/Loading.json
index 4ca5c17..86baace 100644
--- a/./current-build/api/Loading.json
+++ b/./pr-build/api/Loading.json
@@ -6,6 +6,7 @@
   "injection": "$q.loading",
   "quasarConfOptions": {
     "propName": "loading",
+    "type": "Object",
     "definition": {
       "delay": {
         "type": "Number",
diff --git a/./current-build/api/LoadingBar.json b/./pr-build/api/LoadingBar.json
index bbfcc0d..1843183 100644
--- a/./current-build/api/LoadingBar.json
+++ b/./pr-build/api/LoadingBar.json
@@ -6,12 +6,12 @@
   "injection": "$q.loadingBar",
   "quasarConfOptions": {
     "propName": "loadingBar",
-    "definition": {
-      "...props": {
-        "type": "Object",
-        "desc": "QAjaxBar component props, EXCEPT for 'hijack-filter'"
-      }
-    }
+    "type": "Object",
+    "tsType": "QLoadingBarOptions",
+    "desc": "QAjaxBar component props, EXCEPT for 'hijack-filter'",
+    "examples": [
+      "{ position: 'bottom', reverse: true }"
+    ]
   },
   "props": {
     "isActive": {
@@ -49,8 +49,9 @@
       "params": {
         "props": {
           "type": "Object",
+          "tsType": "QLoadingBarOptions",
           "required": true,
-          "desc": "QAjaxBar component props",
+          "desc": "QAjaxBar component props, EXCEPT for 'hijack-filter'",
           "examples": [
             "{ position: 'bottom', reverse: true }"
           ]
diff --git a/./current-build/api/Notify.json b/./pr-build/api/Notify.json
index 444473a..601b216 100644
--- a/./current-build/api/Notify.json
+++ b/./pr-build/api/Notify.json
@@ -6,6 +6,7 @@
   "injection": "$q.notify",
   "quasarConfOptions": {
     "propName": "notify",
+    "type": "Object",
     "definition": {
       "type": {
         "type": "String",
diff --git a/./current-build/api/Ripple.json b/./pr-build/api/Ripple.json
index ae16052..d4b0bfd 100644
--- a/./current-build/api/Ripple.json
+++ b/./pr-build/api/Ripple.json
@@ -5,6 +5,10 @@
   },
   "quasarConfOptions": {
     "propName": "ripple",
+    "type": [
+      "Boolean",
+      "Object"
+    ],
     "definition": {
       "early": {
         "type": "Boolean",
diff --git a/./current-build/api/Screen.json b/./pr-build/api/Screen.json
index 780c537..bc08cc4 100644
--- a/./current-build/api/Screen.json
+++ b/./pr-build/api/Screen.json
@@ -4,6 +4,16 @@
     "docsUrl": "https://v2.quasar.dev/options/screen-plugin"
   },
   "injection": "$q.screen",
+  "quasarConfOptions": {
+    "propName": "screen",
+    "type": "Object",
+    "definition": {
+      "bodyClasses": {
+        "type": "Boolean",
+        "desc": "Whether to apply CSS classes for the current window breakpoint to the body element"
+      }
+    }
+  },
   "props": {
     "width": {
       "type": "Number",

Types

📜 Changes detected:

diff --git a/./pr-build/types/api/qloadingbar.d.ts b/./pr-build/types/api/qloadingbar.d.ts
new file mode 100644
index 0000000..5b436e6
--- /dev/null
+++ b/./pr-build/types/api/qloadingbar.d.ts
@@ -0,0 +1,3 @@
+import { QAjaxBarProps } from "quasar";
+
+export type QLoadingBarOptions = Omit<QAjaxBarProps, "hijackFilter">;
diff --git a/./current-build/types/api.d.ts b/./pr-build/types/api.d.ts
index 255ef27..e17026a 100644
--- a/./current-build/types/api.d.ts
+++ b/./pr-build/types/api.d.ts
@@ -12,6 +12,7 @@ export * from "./api/qnotify";
 export * from "./api/qpopupproxy";
 export * from "./api/qeditor";
 export * from "./api/qloading";
+export * from "./api/qloadingbar";
 export * from "./api/touchswipe";
 export * from "./api/web-storage";
 export * from "./api/validation";
diff --git a/./pr-build/types/config.d.ts b/./pr-build/types/config.d.ts
new file mode 100644
index 0000000..a828f10
--- /dev/null
+++ b/./pr-build/types/config.d.ts
@@ -0,0 +1,13 @@
+interface NativeMobileWrapperConfiguration {
+  iosStatusBarPadding: boolean;
+  backButton: boolean;
+  backButtonExit: boolean | "*" | string[];
+}
+
+export interface QuasarUIConfiguration {
+  // These two are oddly structured and doesn't fit the API structure, so they don't have API definitions
+  capacitor?: NativeMobileWrapperConfiguration;
+  cordova?: NativeMobileWrapperConfiguration;
+
+  // The rest will be augmented by auto-generated code
+}
diff --git a/./current-build/types/index.d.ts b/./pr-build/types/index.d.ts
index a8f29e8..2c73eea 100644
--- a/./current-build/types/index.d.ts
+++ b/./pr-build/types/index.d.ts
@@ -106,7 +106,7 @@ export interface BottomSheet {
       /**
        * Any other custom props
        */
-      [key: string]: any;
+      [key: string]: any | undefined;
     }[];
     /**
      * Display actions as a grid instead of as a list
@@ -322,9 +322,9 @@ export interface LoadingBar {
   increment: (amount?: number) => void;
   /**
    * Set the inner QAjaxBar's props
-   * @param props QAjaxBar component props
+   * @param props QAjaxBar component props, EXCEPT for 'hijack-filter'
    */
-  setDefaults: (props: any) => void;
+  setDefaults: (props: QLoadingBarOptions) => void;
 }
 
 export interface LocalStorage {
@@ -1731,7 +1731,7 @@ export interface QBtnToggleProps {
     /**
      * Any other QBtn props (including class and style)
      */
-    [props: string]: any;
+    [props: string]: any | undefined;
   }[];
   /**
    * Color name for component from the Quasar Color Palette
@@ -3396,14 +3396,12 @@ export interface QEditorProps {
   /**
    * Definition of commands and their buttons to be included in the 'toolbar' prop
    */
-  definitions?:
-    | {
-        /**
-         * Command definition
-         */
-        [commandName: string]: QEditorCommand;
-      }
-    | undefined;
+  definitions?: {
+    /**
+     * Command definition
+     */
+    [commandName: string]: QEditorCommand | undefined;
+  };
   /**
    * Object with definitions of fonts
    */
@@ -4325,9 +4323,6 @@ export interface QFieldSlots {
      */
     emitValue: (value: any) => void;
   }) => VNode[];
-  /**
-   * undefined
-   */
   rawControl: () => VNode[];
 }
 
@@ -6307,26 +6302,24 @@ export interface QOptionGroupProps {
   /**
    * Array of objects with value, label, and disable (optional) props. The binary components will be created according to this array; Props from QToggle, QCheckbox or QRadio can also be added as key/value pairs to control the components singularly
    */
-  options?:
-    | {
-        /**
-         * Label to display along the component
-         */
-        label: string;
-        /**
-         * Value of the option that will be used by the component model
-         */
-        value: any;
-        /**
-         * If true, the option will be disabled
-         */
-        disable?: boolean;
-        /**
-         * Any other props from QToggle, QCheckbox, or QRadio
-         */
-        [props: string]: any;
-      }[]
-    | undefined;
+  options?: {
+    /**
+     * Label to display along the component
+     */
+    label: string;
+    /**
+     * Value of the option that will be used by the component model
+     */
+    value: any;
+    /**
+     * If true, the option will be disabled
+     */
+    disable?: boolean;
+    /**
+     * Any other props from QToggle, QCheckbox, or QRadio
+     */
+    [props: string]: any | undefined;
+  }[];
   /**
    * Used to specify the name of the controls; Useful if dealing with forms submitted directly to a URL
    */
@@ -6392,7 +6385,7 @@ export interface QOptionGroupSlots {
     /**
      * Any other props from QToggle, QCheckbox, or QRadio
      */
-    [props: string]: any;
+    [props: string]: any | undefined;
   }) => VNode[];
   /**
    * Slot to define the specific label for the option at '[name]' where name is a 0-based index; Overrides the generic 'label' slot if used
@@ -6414,7 +6407,7 @@ export interface QOptionGroupSlots {
     /**
      * Any other props from QToggle, QCheckbox, or QRadio
      */
-    [props: string]: any;
+    [props: string]: any | undefined;
   }) => VNode[];
 }
 
@@ -13392,7 +13385,7 @@ export interface QDialogOptions {
         /**
          * See QBtn for available props
          */
-        [props: string]: any;
+        [props: string]: any | undefined;
       }
     | boolean;
   /**
@@ -13404,7 +13397,7 @@ export interface QDialogOptions {
         /**
          * See QBtn for available props
          */
-        [props: string]: any;
+        [props: string]: any | undefined;
       }
     | boolean;
   /**
@@ -13528,6 +13521,7 @@ export interface QLoadingShowOptions {
 }
 
 import { QLoadingUpdateOptions } from "./api";
+import { QLoadingBarOptions } from "./api";
 import { WebStorageGetItemMethodType } from "./api";
 import { WebStorageGetIndexMethodType } from "./api";
 import { WebStorageGetKeyMethodType } from "./api";
@@ -13803,7 +13797,7 @@ declare module "./globals" {
         /**
          * Any other custom props
          */
-        [key: string]: any;
+        [key: string]: any | undefined;
       }[];
       /**
        * Display actions as a grid instead of as a list
@@ -14125,6 +14119,263 @@ declare module "@vue/runtime-core" {
   }
 }
 
+declare module "./config.d.ts" {
+  interface QuasarConfOptions {
+    /**
+     * "'auto'" uses the OS/browser preference. "true" forces dark mode. "false" forces light mode.
+     */
+    dark?: boolean | "auto";
+    loading?: {
+      /**
+       * Wait a number of millisecond before showing; Not worth showing for 100ms for example then hiding it, so wait until you're sure it's a process that will take some considerable amount of time
+       */
+      delay?: number;
+      /**
+       * Message to display
+       */
+      message?: string;
+      /**
+       * Default Loading group name
+       * Default value: __default_quasar_group__
+       */
+      group?: string;
+      /**
+       * Force render the message as HTML; This can lead to XSS attacks so make sure that you sanitize the content
+       */
+      html?: boolean;
+      /**
+       * Content wrapped element custom classes
+       */
+      boxClass?: string;
+      /**
+       * Spinner size (in pixels)
+       */
+      spinnerSize?: number;
+      /**
+       * Color name for spinner from the Quasar Color Palette
+       */
+      spinnerColor?: string;
+      /**
+       * Color name for text from the Quasar Color Palette
+       */
+      messageColor?: string;
+      /**
+       * Color name for background from the Quasar Color Palette
+       */
+      backgroundColor?: string;
+      /**
+       * One of the QSpinners
+       */
+      spinner?: string;
+      /**
+       * Add a CSS class to the container element to easily customize the component
+       */
+      customClass?: string;
+    };
+    /**
+     * QAjaxBar component props, EXCEPT for 'hijack-filter'
+     */
+    loadingBar?: QLoadingBarOptions;
+    notify?: {
+      /**
+       * Optional type (that has been previously registered) or one of the out of the box ones ('positive', 'negative', 'warning', 'info', 'ongoing')
+       */
+      type?: string;
+      /**
+       * Color name for component from the Quasar Color Palette
+       */
+      color?: string;
+      /**
+       * Color name for component from the Quasar Color Palette
+       */
+      textColor?: string;
+      /**
+       * The content of your message
+       */
+      message?: string;
+      /**
+       * The content of your optional caption
+       */
+      caption?: string;
+      /**
+       * Render the message as HTML; This can lead to XSS attacks, so make sure that you sanitize the message first
+       */
+      html?: boolean;
+      /**
+       * Icon name following Quasar convention; Make sure you have the icon library installed unless you are using 'img:' prefix; If 'none' (String) is used as value then no icon is rendered (but screen real estate will still be used for it)
+       */
+      icon?: string;
+      /**
+       * Color name for component from the Quasar Color Palette
+       */
+      iconColor?: string;
+      /**
+       * Size in CSS units, including unit name
+       */
+      iconSize?: string;
+      /**
+       * URL to an avatar/image; Suggestion: use public folder
+       */
+      avatar?: string;
+      /**
+       * Useful for notifications that are updated; Displays a Quasar spinner instead of an avatar or icon; If value is Boolean 'true' then the default QSpinner is shown
+       */
+      spinner?: boolean;
+      /**
+       * Color name for component from the Quasar Color Palette
+       */
+      spinnerColor?: string;
+      /**
+       * Size in CSS units, including unit name
+       */
+      spinnerSize?: string;
+      /**
+       * Window side/corner to stick to
+       * Default value: bottom
+       */
+      position?:
+        | "top-left"
+        | "top-right"
+        | "bottom-left"
+        | "bottom-right"
+        | "top"
+        | "bottom"
+        | "left"
+        | "right"
+        | "center";
+      /**
+       * Override the auto generated group with custom one; Grouped notifications cannot be updated; String or number value inform this is part of a specific group, regardless of its options; When a new notification is triggered with same group name, it replaces the old one and shows a badge with how many times the notification was triggered
+       * Default value: (message + caption + multiline + actions labels + position)
+       */
+      group?: boolean | string | number;
+      /**
+       * Color name for the badge from the Quasar Color Palette
+       */
+      badgeColor?: string;
+      /**
+       * Color name for the badge text from the Quasar Color Palette
+       */
+      badgeTextColor?: string;
+      /**
+       * Notification corner to stick badge to; If notification is on the left side then default is top-right otherwise it is top-left
+       * Default value: (top-left/top-right)
+       */
+      badgePosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
+      /**
+       * Style definitions to be attributed to the badge
+       */
+      badgeStyle?: VueStyleProp;
+      /**
+       * Class definitions to be attributed to the badge
+       */
+      badgeClass?: VueClassProp;
+      /**
+       * Show progress bar to detail when notification will disappear automatically (unless timeout is 0)
+       */
+      progress?: boolean;
+      /**
+       * Class definitions to be attributed to the progress bar
+       */
+      progressClass?: VueClassProp;
+      /**
+       * Add CSS class(es) to the notification for easier customization
+       */
+      classes?: string;
+      /**
+       * Key-value for attributes to be set on the notification
+       */
+      attrs?: any;
+      /**
+       * Amount of time to display (in milliseconds)
+       * Default value: 5000
+       */
+      timeout?: number;
+      /**
+       * Convenient way to add a dismiss button with a specific label, without using the 'actions' prop; If set to true, it uses a label according to the current Quasar language
+       */
+      closeBtn?: boolean | string;
+      /**
+       * Put notification into multi-line mode; If this prop isn't used and more than one 'action' is specified then notification goes into multi-line mode by default
+       */
+      multiLine?: boolean;
+    };
+    screen?: {
+      /**
+       * Whether to apply CSS classes for the current window breakpoint to the body element
+       */
+      bodyClasses?: boolean;
+    };
+    brand?: {
+      /**
+       * Main color of your app
+       */
+      primary?: string;
+      /**
+       * Secondary color of your app
+       */
+      secondary?: string;
+      /**
+       * Accent color of your app
+       */
+      accent?: string;
+      /**
+       * Dark color of your app
+       */
+      dark?: string;
+      /**
+       * Positive color of your app
+       */
+      positive?: string;
+      /**
+       * Negative color of your app
+       */
+      negative?: string;
+      /**
+       * Info color of your app
+       */
+      info?: string;
+      /**
+       * Warning color of your app
+       */
+      warning?: string;
+      /**
+       * Custom colors of your app, if any
+       */
+      [customColors: string]: string | undefined;
+    };
+    lang?: {
+      /**
+       * Whether to disable `dir` and `lang` HTML attributes getting added to the `<html>` tag. `dir` attribute is crucial when using RTL support. Disable this only if you need to handle these yourself for some reason.
+       */
+      noHtmlAttrs?: boolean;
+    };
+    ripple?:
+      | boolean
+      | {
+          /**
+           * Trigger early/immediately on user interaction
+           */
+          early?: boolean;
+          /**
+           * Stop click/touch event propagation
+           */
+          stop?: boolean;
+          /**
+           * Ripple starts from the absolute center
+           */
+          center?: boolean;
+          /**
+           * Color name from Quasar Color Palette; Overrides default dynamic color
+           */
+          color?: string;
+          /**
+           * List of keyCode that should trigger the ripple
+           */
+          keyCodes?: readonly any[] | number;
+        };
+  }
+}
+
 import "./lang";
 declare module "./lang" {
   export interface QuasarLanguageCodesHolder {
@@ -14203,6 +14454,7 @@ export * from "./extras";
 export * from "./lang";
 export * from "./api";
 export * from "./plugin";
+export * from "./config";
 
 export const AddressbarColor: AddressbarColor;
 export const AppFullscreen: AppFullscreen;
diff --git a/./current-build/types/plugin.d.ts b/./pr-build/types/plugin.d.ts
index 8b80851..7421a1c 100644
--- a/./current-build/types/plugin.d.ts
+++ b/./pr-build/types/plugin.d.ts
@@ -1,3 +1,4 @@
+import { QuasarUIConfiguration } from "./config";
 import { QuasarIconSet } from "./extras";
 import { QuasarLanguage } from "./lang";
 
@@ -9,7 +10,7 @@ export interface QuasarPlugins {}
 
 export interface QuasarPluginOptions {
   lang: QuasarLanguage;
-  config: any;
+  config: QuasarUIConfiguration;
   iconSet: QuasarIconSet;
   components: QuasarComponents;
   directives: QuasarDirectives;

@github-actions
Copy link

github-actions bot commented Jun 5, 2023

UI Tests Results

0 tests   0 ✔️  0s ⏱️
0 suites  0 💤
0 files    0

Results for commit e2c1a15.

@rstoenescu rstoenescu merged commit 3c248e8 into quasarframework:dev Jun 10, 2023
3 of 5 checks passed
@rstoenescu
Copy link
Member

Great work!

@yusufkandemir yusufkandemir deleted the feat-generate-types-for-quasar-conf-options-and-improve-docs branch June 10, 2023 10:24
@dmnkrthrt
Copy link

dmnkrthrt commented Jun 29, 2023

@rstoenescu & @yusufkandemir

What's the purpose of removing hijackFilter() from QLoadingBarOptions? How do I skip routes from a QLoadingBar now? Your docs still mention hijackFilter() in LoadingBar.setDefaults().

Thank you.

@yusufkandemir
Copy link
Member Author

@dmnkrthrt as hijackFilter is a function, it can't be serialized, so it can't be accepted from quasar.config file. However, as you correctly pointed out, it's possible to set it through LoadingBar.setDefaults(). It's also possible to set it through the Quasar plugin options(app.use(Quasar, { ... })) when using flavors other than Quasar CLI. So, the type should only be disallowed in quasar.config file. Thanks for raising awareness, I'll address this.

@dmnkrthrt
Copy link

@yusufkandemir you wrote it's possible to set it via LoadingBar.setDefaults() - but I can't confirm that (and that's exactly my issue). See vue-tsc output:

error TS2345: Argument of type '{ color: string; size: string; hijackFilter(url: string): boolean; }' is not assignable to parameter of type 'QLoadingBarOptions'. Object literal may only specify known properties, and 'hijackFilter' does not exist in type 'QLoadingBarOptions'.

Using app.use(...config: { loadingBar: ... }) results in another TS error:

error TS2769: No overload matches this call. Overload 1 of 2, '(plugin: Plugin<[options: Partial<QuasarPluginOptions>]>, options: Partial<QuasarPluginOptions>): App<Element>', gave the following error. Type '{ loadingBar: { color: string; size: string; hijackFilter(url: string): boolean; }; }' is not assignable to type 'QuasarUIConfiguration'.

@yusufkandemir
Copy link
Member Author

@dmnkrthrt I meant from a functionality perspective. If you bypass the type errors, you will see that it works correctly.

I included a fix in #16049

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update quasarConfOptions and generate TS types for it
3 participants