Browse Source

HUE-9485 [frontend] Extract a generic DropdownPanel component from the DropdownMenu

This also improves some component naming
Johan Ahlen 5 years ago
parent
commit
96c3d0364e
20 changed files with 258 additions and 147 deletions
  1. 7 7
      desktop/core/src/desktop/js/components/Paginator.vue
  2. 8 8
      desktop/core/src/desktop/js/components/__snapshots__/Paginator.test.ts.snap
  3. 4 4
      desktop/core/src/desktop/js/components/dropdown/DropdownMenu.test.ts
  4. 93 0
      desktop/core/src/desktop/js/components/dropdown/DropdownMenu.vue
  5. 3 3
      desktop/core/src/desktop/js/components/dropdown/DropdownMenuButton.test.ts
  6. 1 1
      desktop/core/src/desktop/js/components/dropdown/DropdownMenuButton.vue
  7. 3 3
      desktop/core/src/desktop/js/components/dropdown/DropdownMenuGroup.test.ts
  8. 1 1
      desktop/core/src/desktop/js/components/dropdown/DropdownMenuGroup.vue
  9. 3 3
      desktop/core/src/desktop/js/components/dropdown/DropdownMenuItem.test.ts
  10. 1 1
      desktop/core/src/desktop/js/components/dropdown/DropdownMenuItem.vue
  11. 3 3
      desktop/core/src/desktop/js/components/dropdown/DropdownMenuText.test.ts
  12. 1 1
      desktop/core/src/desktop/js/components/dropdown/DropdownMenuText.vue
  13. 34 0
      desktop/core/src/desktop/js/components/dropdown/DropdownPanel.test.ts
  14. 51 94
      desktop/core/src/desktop/js/components/dropdown/DropdownPanel.vue
  15. 31 0
      desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownMenu.test.ts.snap
  16. 1 1
      desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownMenuButton.test.ts.snap
  17. 1 1
      desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownMenuGroup.test.ts.snap
  18. 1 1
      desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownMenuItem.test.ts.snap
  19. 1 1
      desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownMenuText.test.ts.snap
  20. 10 14
      desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownPanel.test.ts.snap

+ 7 - 7
desktop/core/src/desktop/js/components/Paginator.vue

@@ -22,15 +22,15 @@
       {{ offset + 1 }}-{{ Math.min(offset + limit, totalEntries) }} of {{ totalEntries }}
     </div>
     Rows per page:
-    <dropdown :inline="true" :text="limit">
-      <dropdown-item-button
+    <dropdown-menu :inline="true" :text="limit">
+      <dropdown-menu-button
         v-for="presetLimit of presetLimits"
         :key="presetLimit"
         @click="setLimit(presetLimit)"
       >
         {{ presetLimit }}
-      </dropdown-item-button>
-    </dropdown>
+      </dropdown-menu-button>
+    </dropdown-menu>
     <div class="navigation-actions">
       <a
         href="javascript: void(0);"
@@ -66,8 +66,8 @@
 
 <script lang="ts">
   import HueIcon from './HueIcon.vue';
-  import Dropdown from './dropdown/Dropdown.vue';
-  import DropdownItemButton from './dropdown/DropdownItemButton.vue';
+  import DropdownMenu from './dropdown/DropdownMenu.vue';
+  import DropdownMenuButton from './dropdown/DropdownMenuButton.vue';
   import Vue from 'vue';
   import Component from 'vue-class-component';
   import { Prop, Watch } from 'vue-property-decorator';
@@ -76,7 +76,7 @@
   const PRESET_LIMITS = [DEFAULT_LIMIT, 50, 100];
 
   @Component({
-    components: { HueIcon, Dropdown, DropdownItemButton }
+    components: { HueIcon, DropdownMenu, DropdownMenuButton }
   })
   export default class Paginator extends Vue {
     @Prop({ required: true })

+ 8 - 8
desktop/core/src/desktop/js/components/__snapshots__/Paginator.test.ts.snap

@@ -14,26 +14,26 @@ exports[`Paginator.vue should render 1`] = `
   
   Rows per page:
   
-  <dropdown-stub
+  <dropdown-menu-stub
     inline="true"
     text="25"
   >
-    <dropdown-item-button-stub>
+    <dropdown-menu-button-stub>
       
       25
     
-    </dropdown-item-button-stub>
-    <dropdown-item-button-stub>
+    </dropdown-menu-button-stub>
+    <dropdown-menu-button-stub>
       
       50
     
-    </dropdown-item-button-stub>
-    <dropdown-item-button-stub>
+    </dropdown-menu-button-stub>
+    <dropdown-menu-button-stub>
       
       100
     
-    </dropdown-item-button-stub>
-  </dropdown-stub>
+    </dropdown-menu-button-stub>
+  </dropdown-menu-stub>
    
   <div
     class="navigation-actions"

+ 4 - 4
desktop/core/src/desktop/js/components/dropdown/Dropdown.test.ts → desktop/core/src/desktop/js/components/dropdown/DropdownMenu.test.ts

@@ -15,16 +15,16 @@
 // limitations under the License.
 
 import { shallowMount } from '@vue/test-utils';
-import Dropdown from './Dropdown.vue';
+import DropdownMenu from './DropdownMenu.vue';
 
-describe('Dropdown.vue', () => {
+describe('DropdownMenu.vue', () => {
   it('should render empty dropdown', () => {
-    const wrapper = shallowMount(Dropdown);
+    const wrapper = shallowMount(DropdownMenu);
     expect(wrapper.element).toMatchSnapshot();
   });
 
   it('should render dropdown with slots', () => {
-    const wrapper = shallowMount(Dropdown, {
+    const wrapper = shallowMount(DropdownMenu, {
       scopedSlots: {
         default: '<div>Some item</div>'
       }

+ 93 - 0
desktop/core/src/desktop/js/components/dropdown/DropdownMenu.vue

@@ -0,0 +1,93 @@
+<!--
+  Licensed to Cloudera, Inc. under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  Cloudera, Inc. licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<template>
+  <dropdown-panel class="hue-dropdown-menu" :text="text" :inline="inline">
+    <div class="hue-dropdown-menu-inner">
+      <ul>
+        <slot />
+      </ul>
+    </div>
+  </dropdown-panel>
+</template>
+
+<script lang="ts">
+  import DropdownPanel from './DropdownPanel.vue';
+  import Vue from 'vue';
+  import Component from 'vue-class-component';
+  import { Prop } from 'vue-property-decorator';
+
+  @Component({
+    components: { DropdownPanel }
+  })
+  export default class DropdownMenu extends Vue {
+    @Prop({ required: false, default: '' })
+    text: string;
+    @Prop({ required: false, default: false })
+    inline: boolean;
+  }
+</script>
+
+<style lang="scss" scoped>
+  @import '../styles/colors';
+
+  .hue-dropdown-menu {
+    .hue-dropdown-menu-inner {
+      min-width: 160px;
+      max-width: 250px;
+      min-height: 34px;
+      max-height: 200px;
+
+      overflow-x: hidden;
+      overflow-y: auto;
+
+      /deep/ ul {
+        overflow-x: hidden;
+        margin: 0 !important;
+        padding: 0;
+        list-style: none;
+        font-size: 13px;
+
+        li {
+          color: $fluid-gray-800;
+
+          button,
+          a {
+            display: block;
+            width: 100%;
+            padding: 6px 16px;
+            clear: both;
+            font-weight: 400;
+            text-align: inherit;
+            white-space: nowrap;
+            background-color: transparent;
+            border: 0;
+            position: relative;
+            outline: 0;
+
+            &:hover,
+            &.active,
+            &.focus {
+              background-color: $hue-primary-color-light;
+            }
+          }
+        }
+      }
+    }
+  }
+</style>

+ 3 - 3
desktop/core/src/desktop/js/components/dropdown/DropdownItemButton.test.ts → desktop/core/src/desktop/js/components/dropdown/DropdownMenuButton.test.ts

@@ -15,11 +15,11 @@
 // limitations under the License.
 
 import { shallowMount } from '@vue/test-utils';
-import DropdownItemButton from './DropdownItemButton.vue';
+import DropdownMenuButton from './DropdownMenuButton.vue';
 
-describe('DropdownItemButton.vue', () => {
+describe('DropdownMenuButton.vue', () => {
   it('should render', () => {
-    const wrapper = shallowMount(DropdownItemButton);
+    const wrapper = shallowMount(DropdownMenuButton);
     expect(wrapper.element).toMatchSnapshot();
   });
 });

+ 1 - 1
desktop/core/src/desktop/js/components/dropdown/DropdownItemButton.vue → desktop/core/src/desktop/js/components/dropdown/DropdownMenuButton.vue

@@ -27,7 +27,7 @@
   import Component from 'vue-class-component';
 
   @Component
-  export default class DropdownItemButton extends Vue {}
+  export default class DropdownMenuButton extends Vue {}
 </script>
 
 <style lang="scss" scoped></style>

+ 3 - 3
desktop/core/src/desktop/js/components/dropdown/DropdownGroup.test.ts → desktop/core/src/desktop/js/components/dropdown/DropdownMenuGroup.test.ts

@@ -15,11 +15,11 @@
 // limitations under the License.
 
 import { shallowMount } from '@vue/test-utils';
-import DropdownGroup from './DropdownGroup.vue';
+import DropdownMenuGroup from './DropdownMenuGroup.vue';
 
-describe('DropdownGroup.vue', () => {
+describe('DropdownMenuGroup.vue', () => {
   it('should render', () => {
-    const wrapper = shallowMount(DropdownGroup, {
+    const wrapper = shallowMount(DropdownMenuGroup, {
       propsData: {
         header: 'Some Header'
       }

+ 1 - 1
desktop/core/src/desktop/js/components/dropdown/DropdownGroup.vue → desktop/core/src/desktop/js/components/dropdown/DropdownMenuGroup.vue

@@ -31,7 +31,7 @@
   import { Prop } from 'vue-property-decorator';
 
   @Component
-  export default class DropdownGroup extends Vue {
+  export default class DropdownMenuGroup extends Vue {
     @Prop({ required: true })
     header!: string;
   }

+ 3 - 3
desktop/core/src/desktop/js/components/dropdown/DropdownItemText.test.ts → desktop/core/src/desktop/js/components/dropdown/DropdownMenuItem.test.ts

@@ -15,11 +15,11 @@
 // limitations under the License.
 
 import { shallowMount } from '@vue/test-utils';
-import DropdownItemText from './DropdownItemText.vue';
+import DropdownMenuItem from './DropdownMenuItem.vue';
 
-describe('DropdownItemText.vue', () => {
+describe('DropdownMenuItem.vue', () => {
   it('should render', () => {
-    const wrapper = shallowMount(DropdownItemText);
+    const wrapper = shallowMount(DropdownMenuItem);
     expect(wrapper.element).toMatchSnapshot();
   });
 });

+ 1 - 1
desktop/core/src/desktop/js/components/dropdown/DropdownItem.vue → desktop/core/src/desktop/js/components/dropdown/DropdownMenuItem.vue

@@ -28,7 +28,7 @@
   import Component from 'vue-class-component';
 
   @Component
-  export default class DropdownItem extends Vue {}
+  export default class DropdownMenuItem extends Vue {}
 </script>
 
 <style lang="scss" scoped></style>

+ 3 - 3
desktop/core/src/desktop/js/components/dropdown/DropdownItem.test.ts → desktop/core/src/desktop/js/components/dropdown/DropdownMenuText.test.ts

@@ -15,11 +15,11 @@
 // limitations under the License.
 
 import { shallowMount } from '@vue/test-utils';
-import DropdownItem from './DropdownItem.vue';
+import DropdownMenuText from './DropdownMenuText.vue';
 
-describe('DropdownItem.vue', () => {
+describe('DropdownMenuText.vue', () => {
   it('should render', () => {
-    const wrapper = shallowMount(DropdownItem);
+    const wrapper = shallowMount(DropdownMenuText);
     expect(wrapper.element).toMatchSnapshot();
   });
 });

+ 1 - 1
desktop/core/src/desktop/js/components/dropdown/DropdownItemText.vue → desktop/core/src/desktop/js/components/dropdown/DropdownMenuText.vue

@@ -29,7 +29,7 @@
   import { Prop } from 'vue-property-decorator';
 
   @Component
-  export default class DropdownItemText extends Vue {
+  export default class DropdownMenuText extends Vue {
     @Prop({ required: false, default: '' })
     text;
   }

+ 34 - 0
desktop/core/src/desktop/js/components/dropdown/DropdownPanel.test.ts

@@ -0,0 +1,34 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// 'License'); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an 'AS IS' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { shallowMount } from '@vue/test-utils';
+import DropdownPanel from './DropdownPanel.vue';
+
+describe('DropdownPanel.vue', () => {
+  it('should render empty dropdown panel', () => {
+    const wrapper = shallowMount(DropdownPanel);
+    expect(wrapper.element).toMatchSnapshot();
+  });
+
+  it('should render dropdown panel with slots', () => {
+    const wrapper = shallowMount(DropdownPanel, {
+      scopedSlots: {
+        default: '<div>Some item</div>'
+      }
+    });
+    expect(wrapper.element).toMatchSnapshot();
+  });
+});

+ 51 - 94
desktop/core/src/desktop/js/components/dropdown/Dropdown.vue → desktop/core/src/desktop/js/components/dropdown/DropdownPanel.vue

@@ -17,19 +17,21 @@
 -->
 
 <template>
-  <div v-click-outside="clickOutside" :class="{ 'dropdown-inline': inline }">
-    <hue-link v-if="inline" @click="toggleMenu">
+  <div
+    v-click-outside="clickOutside"
+    class="hue-dropdown-panel"
+    :class="{ 'dropdown-inline': inline }"
+  >
+    <hue-link v-if="inline" @click="togglePanel">
       {{ text }} <i class="fa fa-caret-down" />
     </hue-link>
-    <hue-button v-else @click="toggleMenu"> {{ text }} <i class="fa fa-caret-down" /></hue-button>
-    <div :class="{ open: menuOpen }" class="hue-dropdown-container" @click="toggleMenu">
+    <hue-button v-else @click="togglePanel"> {{ text }} <i class="fa fa-caret-down" /></hue-button>
+    <div class="hue-dropdown-container" :class="{ open: panelOpen }" @click="togglePanel">
       <div
-        class="hue-dropdown-menu"
+        class="hue-dropdown-inner"
         :class="{ 'position-top': positionTop, 'position-left': positionLeft }"
       >
-        <ul>
-          <slot />
-        </ul>
+        <slot />
       </div>
     </div>
   </div>
@@ -66,18 +68,18 @@
       'click-outside': clickOutsideDirective
     }
   })
-  export default class Dropdown extends Vue {
+  export default class DropdownPanel extends Vue {
     @Prop({ required: false, default: '' })
     text: string;
     @Prop({ required: false, default: false })
     inline: boolean;
-    menuOpen = false;
+    panelOpen = false;
 
     positionTop = false;
     positionLeft = false;
 
-    toggleMenu(): void {
-      if (!this.menuOpen) {
+    togglePanel(): void {
+      if (!this.panelOpen) {
         const dropdownElement = this.$el.getElementsByClassName('hue-dropdown-container');
         if (dropdownElement.length) {
           const outsideStatus = isOutsideViewport(dropdownElement[0]);
@@ -85,12 +87,12 @@
           this.positionLeft = outsideStatus.right;
         }
       }
-      this.menuOpen = !this.menuOpen;
+      this.panelOpen = !this.panelOpen;
     }
 
     clickOutside(): void {
-      if (this.menuOpen) {
-        this.menuOpen = false;
+      if (this.panelOpen) {
+        this.panelOpen = false;
       }
     }
   }
@@ -100,94 +102,49 @@
   @import '../styles/colors';
   @import '../styles/mixins';
 
-  .dropdown-inline {
-    display: inline-block;
-  }
-
-  .hue-dropdown-container {
-    position: fixed;
-    z-index: 1061;
-
-    &.open {
-      position: absolute;
-      .hue-dropdown-menu {
-        display: block;
-      }
-    }
-
-    &.hue-dropdown-fixed {
-      position: fixed !important;
+  .hue-dropdown-panel {
+    &.dropdown-inline {
+      display: inline-block;
     }
 
-    .hue-dropdown-menu {
-      display: none;
-      z-index: 1000;
-      float: left;
-
-      position: absolute;
-
-      margin: 2px 0 0;
-      padding: 0;
-      min-width: 160px;
-      max-width: 250px;
-      min-height: 34px;
-      max-height: 200px;
+    .hue-dropdown-container {
+      position: fixed;
+      z-index: 1061;
 
-      background-color: $fluid-white;
-      overflow-x: hidden;
-      overflow-y: auto;
-      list-style: none;
-      border: 1px solid $hue-border-color;
-      border-radius: $hue-panel-border-radius;
+      &.open {
+        position: absolute;
+        .hue-dropdown-inner {
+          display: block;
+        }
+      }
 
-      @include box-shadow(0, 5px, 10px, rgba(0, 0, 0, 0.2));
+      .hue-dropdown-inner {
+        display: none;
+        z-index: 1000;
+        float: left;
+        position: absolute;
+        margin: 2px 0 0;
+        padding: 0;
+        background-color: $fluid-white;
+        border: 1px solid $hue-border-color;
+        border-radius: $hue-panel-border-radius;
 
-      &:not(.position-top) {
-        top: 100%;
-      }
+        @include box-shadow(0, 5px, 10px, rgba(0, 0, 0, 0.2));
 
-      &.position-top {
-        bottom: 20px; // TODO: Calculate based on link/button dimensions
-      }
+        &:not(.position-top) {
+          top: 100%;
+        }
 
-      &:not(.position-left) {
-        left: 0;
-      }
+        &.position-top {
+          bottom: 20px; // TODO: Calculate based on link/button dimensions
+        }
 
-      &.position-left {
-        right: 0; // TODO: Calculate based on link/button dimensions
-      }
+        &:not(.position-left) {
+          left: 0;
+        }
 
-      /deep/ ul {
-        overflow-x: hidden;
-        margin: 0 !important;
-        padding: 0;
-        list-style: none;
-        font-size: 13px;
-
-        li {
-          color: $fluid-gray-800;
-
-          button,
-          a {
-            display: block;
-            width: 100%;
-            padding: 6px 16px;
-            clear: both;
-            font-weight: 400;
-            text-align: inherit;
-            white-space: nowrap;
-            background-color: transparent;
-            border: 0;
-            position: relative;
-            outline: 0;
-
-            &:hover,
-            &.active,
-            &.focus {
-              background-color: $hue-primary-color-light;
-            }
-          }
+        &.position-left {
+          right: 0; // TODO: Calculate based on link/button dimensions
         }
       }
     }

+ 31 - 0
desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownMenu.test.ts.snap

@@ -0,0 +1,31 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`DropdownMenu.vue should render dropdown with slots 1`] = `
+<dropdown-panel-stub
+  class="hue-dropdown-menu"
+  text=""
+>
+  <div
+    class="hue-dropdown-menu-inner"
+  >
+    <ul>
+      <div>
+        Some item
+      </div>
+    </ul>
+  </div>
+</dropdown-panel-stub>
+`;
+
+exports[`DropdownMenu.vue should render empty dropdown 1`] = `
+<dropdown-panel-stub
+  class="hue-dropdown-menu"
+  text=""
+>
+  <div
+    class="hue-dropdown-menu-inner"
+  >
+    <ul />
+  </div>
+</dropdown-panel-stub>
+`;

+ 1 - 1
desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownItemButton.test.ts.snap → desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownMenuButton.test.ts.snap

@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`DropdownItemButton.vue should render 1`] = `
+exports[`DropdownMenuButton.vue should render 1`] = `
 <li>
   <button />
 </li>

+ 1 - 1
desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownGroup.test.ts.snap → desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownMenuGroup.test.ts.snap

@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`DropdownGroup.vue should render 1`] = `
+exports[`DropdownMenuGroup.vue should render 1`] = `
 <li>
   <header
     class="dropdown-group-header"

+ 1 - 1
desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownItem.test.ts.snap → desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownMenuItem.test.ts.snap

@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`DropdownItem.vue should render 1`] = `
+exports[`DropdownMenuItem.vue should render 1`] = `
 <li>
   
   

+ 1 - 1
desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownItemText.test.ts.snap → desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownMenuText.test.ts.snap

@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`DropdownItemText.vue should render 1`] = `
+exports[`DropdownMenuText.vue should render 1`] = `
 <li
   class="hue-dropdown-item-text"
 >

+ 10 - 14
desktop/core/src/desktop/js/components/dropdown/__snapshots__/Dropdown.test.ts.snap → desktop/core/src/desktop/js/components/dropdown/__snapshots__/DropdownPanel.test.ts.snap

@@ -1,8 +1,8 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Dropdown.vue should render dropdown with slots 1`] = `
+exports[`DropdownPanel.vue should render dropdown panel with slots 1`] = `
 <div
-  class=""
+  class="hue-dropdown-panel"
 >
   <hue-button-stub>
       
@@ -15,21 +15,19 @@ exports[`Dropdown.vue should render dropdown with slots 1`] = `
     class="hue-dropdown-container"
   >
     <div
-      class="hue-dropdown-menu"
+      class="hue-dropdown-inner"
     >
-      <ul>
-        <div>
-          Some item
-        </div>
-      </ul>
+      <div>
+        Some item
+      </div>
     </div>
   </div>
 </div>
 `;
 
-exports[`Dropdown.vue should render empty dropdown 1`] = `
+exports[`DropdownPanel.vue should render empty dropdown panel 1`] = `
 <div
-  class=""
+  class="hue-dropdown-panel"
 >
   <hue-button-stub>
       
@@ -42,10 +40,8 @@ exports[`Dropdown.vue should render empty dropdown 1`] = `
     class="hue-dropdown-container"
   >
     <div
-      class="hue-dropdown-menu"
-    >
-      <ul />
-    </div>
+      class="hue-dropdown-inner"
+    />
   </div>
 </div>
 `;