Caleb Porzio 2 年之前
父节点
当前提交
90797e0d2a
共有 3 个文件被更改,包括 85 次插入11 次删除
  1. 4 0
      packages/ui/src/dialog.js
  2. 28 11
      packages/ui/src/popover.js
  3. 53 0
      tests/cypress/integration/plugins/ui/popover.spec.js

+ 4 - 0
packages/ui/src/dialog.js

@@ -12,9 +12,13 @@ export default function (Alpine) {
         let $data = Alpine.$data(el)
 
         return {
+            // Kept here for legacy. Remove after out of beta.
             get open() {
                 return $data.__isOpen
             },
+            get isOpen() {
+                return $data.__isOpen
+            },
             close() {
                 $data.__close()
             }

+ 28 - 11
packages/ui/src/popover.js

@@ -12,9 +12,15 @@ export default function (Alpine) {
         let $data = Alpine.$data(el)
 
         return {
-            get open() {
+            get isOpen() {
                 return $data.__isOpenState
-            }
+            },
+            open() {
+                $data.__open()
+            },
+            close() {
+                $data.__close()
+            },
         }
     })
 }
@@ -36,6 +42,12 @@ function handleRoot(el, Alpine) {
                 },
                 __buttonEl: undefined,
                 __panelEl: undefined,
+                __isStatic: false,
+                get __isOpen() {
+                    if (this.__isStatic) return true
+
+                    return this.__isOpenState
+                },
                 __isOpenState: false,
                 __open() {
                     this.__isOpenState = true
@@ -46,6 +58,8 @@ function handleRoot(el, Alpine) {
                     this.__isOpenState ? this.__close() : this.__open()
                 },
                 __close(el) {
+                    if (this.__isStatic) return
+
                     this.__isOpenState = false
 
                     if (el === false) return
@@ -84,8 +98,8 @@ function handleButton(el, Alpine) {
     Alpine.bind(el, {
         'x-ref': 'button',
         ':id'() { return this.$id('alpine-popover-button') },
-        ':aria-expanded'() { return this.$data.__isOpenState },
-        ':aria-controls'() { return this.$data.__isOpenState && this.$id('alpine-popover-panel') },
+        ':aria-expanded'() { return this.$data.__isOpen },
+        ':aria-controls'() { return this.$data.__isOpen && this.$id('alpine-popover-panel') },
         'x-init'() {
             if (this.$el.tagName.toLowerCase() === 'button' && !this.$el.hasAttribute('type')) this.$el.type = 'button'
 
@@ -93,7 +107,7 @@ function handleButton(el, Alpine) {
         },
         '@click'() { this.$data.__toggle() },
         '@keydown.tab'(e) {
-            if (! e.shiftKey && this.$data.__isOpenState) {
+            if (! e.shiftKey && this.$data.__isOpen) {
                 let firstFocusableEl = this.$focus.within(this.$data.__panelEl).getFirst()
 
                 if (firstFocusableEl) {
@@ -105,7 +119,7 @@ function handleButton(el, Alpine) {
             }
         },
         '@keyup.tab'(e) {
-            if (this.$data.__isOpenState) {
+            if (this.$data.__isOpen) {
                 // Check if the last focused element was "after" this one
                 let lastEl = this.$focus.previouslyFocused()
 
@@ -133,15 +147,18 @@ function handleButton(el, Alpine) {
 
 function handlePanel(el, Alpine) {
     Alpine.bind(el, {
-        'x-init'() { this.$data.__panelEl = this.$el },
+        'x-init'() {
+            this.$data.__isStatic = Alpine.bound(this.$el, 'static', false)
+            this.$data.__panelEl = this.$el
+        },
         'x-effect'() {
-            this.$data.__isOpenState && Alpine.bound(el, 'focus') && this.$focus.first()
+            this.$data.__isOpen && Alpine.bound(el, 'focus') && this.$focus.first()
         },
         'x-ref': 'panel',
         ':id'() { return this.$id('alpine-popover-panel') },
-        'x-show'() { return this.$data.__isOpenState },
+        'x-show'() { return this.$data.__isOpen },
         '@mousedown.window'($event) {
-            if (! this.$data.__isOpenState) return
+            if (! this.$data.__isOpen) return
             if (this.$data.__contains(this.$data.__buttonEl, $event.target)) return
             if (this.$data.__contains(this.$el, $event.target)) return
 
@@ -187,6 +204,6 @@ function handleGroup(el, Alpine) {
 
 function handleOverlay(el, Alpine) {
     Alpine.bind(el, {
-        'x-show'() { return this.$data.__isOpenState }
+        'x-show'() { return this.$data.__isOpen }
     })
 }

+ 53 - 0
tests/cypress/integration/plugins/ui/popover.spec.js

@@ -19,6 +19,23 @@ test('button toggles panel',
     },
 )
 
+test('popover can be rendered statically',
+    [html`
+        <div x-data x-popover>
+            <button x-popover:button>Toggle</button>
+
+            <ul x-popover:panel static>
+                Dialog Contents!
+            </ul>
+        </div>
+    `],
+    ({ get }) => {
+        get('ul').should(beVisible())
+        get('button').click()
+        get('ul').should(beVisible())
+    },
+)
+
 test('has accessibility attributes',
     [html`
         <div x-data x-popover>
@@ -138,3 +155,39 @@ test('focusing away doesnt close panel if focusing inside a group',
         get('#2 ul').should(notBeVisible())
     },
 )
+
+test('focusing away still closes panel inside a group if the focus attribute is present',
+    [html`
+        <div x-data>
+            <div x-popover:group>
+                <div x-data x-popover id="1">
+                    <button x-popover:button>Toggle 1</button>
+                    <ul x-popover:panel focus>
+                        Dialog 1 Contents!
+                    </ul>
+                </div>
+                <div x-data x-popover id="2">
+                    <button x-popover:button>Toggle 2</button>
+                    <ul x-popover:panel>
+                        Dialog 2 Contents!
+                    </ul>
+                </div>
+            </div>
+
+            <a href="#">Focus Me</a>
+        </div>
+    `],
+    ({ get }) => {
+        get('#1 ul').should(notBeVisible())
+        get('#2 ul').should(notBeVisible())
+        get('#1 button').click()
+        get('#1 ul').should(beVisible())
+        get('#2 ul').should(notBeVisible())
+        cy.focused().tab()
+        get('#1 ul').should(beVisible())
+        get('#2 ul').should(notBeVisible())
+        cy.focused().tab()
+        get('#1 ul').should(notBeVisible())
+        get('#2 ul').should(notBeVisible())
+    },
+)