Repository: postcss/autoprefixer (js)

Issue:
CSS Grid - Sequence of Breakpoints
Another error. I process a grid using two breakpoints, that is, three grid templates. 

The order is No Breakpoint > from 768px > 960px. 

If Autoprefixer now creates the fallbacks for Internet Explorer, the order of the breakpoints does not match. 

```scss
.c-footerNav {
  display: grid;
  grid-gap: 30px 0;
  grid-template:
    "fnbox1" auto
    "fnbox2" auto
    "fnbox3" auto
    "fnbox4" auto /
     1fr;

  @include break('m') {
    grid-gap: 40px;
    grid-template:
      "fnbox1 fnbox1 fnbox1" auto
      "fnbox2 fnbox3 fnbox4" auto /
       1fr  1fr  1fr
  }

  @include break('ml') {
    grid-gap: 60px 40px;
    grid-template:
      "fnbox1 fnbox2 fnbox3 fnbox4" auto /
       1fr  1fr  1fr  1fr;
  }
  &__box1 {
    grid-area: fnbox1;
  }
  &__box2 {
    grid-area: fnbox2;
  }
  &__box3 {
    grid-area: fnbox3;
  }
  &__box4 {
    grid-area: fnbox4;
  }
}
```

Output

```css
.c-footerNav {
  display: -ms-grid;
  display: grid;
  grid-gap: 30px 0;
  -ms-grid-rows: auto 30px auto 30px auto 30px auto;
  -ms-grid-columns: 1fr;
  grid-template: "fnbox1" auto "fnbox2" auto "fnbox3" auto "fnbox4" auto / 1fr;
}

@media screen and (min-width: 768px) {
  .c-footerNav {
    grid-gap: 40px;
    -ms-grid-rows: auto 40px auto;
    -ms-grid-columns: 1fr 40px 1fr 40px 1fr;
    grid-template: "fnbox1 fnbox1 fnbox1" auto "fnbox2 fnbox3 fnbox4" auto / 1fr 1fr 1fr;
  }
}

@media screen and (min-width: 960px) {
  .c-footerNav {
    grid-gap: 60px 40px;
    -ms-grid-rows: auto;
    -ms-grid-columns: 1fr 40px 1fr 40px 1fr 40px 1fr;
    grid-template: "fnbox1 fnbox2 fnbox3 fnbox4" auto / 1fr 1fr 1fr 1fr;
  }
}

.c-footerNav__box1 {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
  grid-area: fnbox1;
}

.c-footerNav__box2 {
  -ms-grid-row: 3;
  -ms-grid-column: 1;
  grid-area: fnbox2;
}

.c-footerNav__box3 {
  -ms-grid-row: 5;
  -ms-grid-column: 1;
  grid-area: fnbox3;
}

.c-footerNav__box4 {
  -ms-grid-row: 7;
  -ms-grid-column: 1;
  grid-area: fnbox4;
}

@media screen and (min-width: 960px) {
  .c-footerNav__box1 {
    -ms-grid-row: 1;
    -ms-grid-column: 1;
  }
  .c-footerNav__box2 {
    -ms-grid-row: 1;
    -ms-grid-column: 3;
  }
  .c-footerNav__box3 {
    -ms-grid-row: 1;
    -ms-grid-column: 5;
  }
  .c-footerNav__box4 {
    -ms-grid-row: 1;
    -ms-grid-column: 7;
  }
}

@media screen and (min-width: 768px) {
  .c-footerNav__box1 {
    -ms-grid-row: 1;
    -ms-grid-column: 1;
    -ms-grid-column-span: 5;
  }
  .c-footerNav__box2 {
    -ms-grid-row: 3;
    -ms-grid-column: 1;
  }
  .c-footerNav__box3 {
    -ms-grid-row: 3;
    -ms-grid-column: 3;
  }
  .c-footerNav__box4 {
    -ms-grid-row: 3;
    -ms-grid-column: 5;
  }
}
```

What else struck me, the grid area names don't seem to be scoped. If I use "box1" several times as grid area name in various grids, the autoprefixer will no longer create fallbacks for subsequent grids. 

Pull request:
[grid-area] fix sequence of breakpoints
This PR fixes #1085

The logic of this fix is simple:
* As the rules being packed into a single the media rule (1), check the closest rule down below. If it's a media rule (2) and it contains the same selectors - insert (1) after (2).

You can check out  the test cases to review is everything ok with the order.

Thanks! :)

Patch:
diff --git a/lib/hacks/grid-utils.js b/lib/hacks/grid-utils.js
index 8b54381..0e761b9 100644
--- a/lib/hacks/grid-utils.js
+++ b/lib/hacks/grid-utils.js
@@ -420,6 +420,40 @@ function shouldAddSpan (decl, area, areaName) {
   return overrideValues
 }
 
+/**
+ * search the next siblings and find the latest media with
+ * the same selectors inside
+ * @param  {Rule | AtRule} rule
+ * @param  {Array<String>} selectors
+ * @return {AtRule}
+ */
+function getLastNextSimilarMedia (rule, selectors) {
+  if (!rule) {
+    return false
+  } else if (
+    rule.type !== 'atrule' &&
+    rule.name !== 'media'
+  ) {
+    return false
+  }
+
+  let selectorsToCompare = []
+  rule.walkRules(r => selectorsToCompare.push(r.selector))
+
+  // check if every selector match
+  let selectorsEqual = selectors.every((sel, index) => {
+    return sel === selectorsToCompare[index]
+  })
+
+  if (selectorsEqual) {
+    // to check the next rule
+    return getLastNextSimilarMedia(rule.next(), selectors)
+  } else {
+    // or return the last similar media
+    return rule.prev()
+  }
+}
+
 function insertAreas (areas, decl, result) {
   let missed = Object.keys(areas)
 
@@ -467,11 +501,20 @@ function insertAreas (areas, decl, result) {
                     /^-ms-/.test(next.first.first.prop)
         ) return undefined
 
+        let selectors = rules.map(r => r.selector)
+        let lastSimilarMedia = getLastNextSimilarMedia(next, selectors)
+
         let areaParentMedia = getParentMedia(gridArea.parent)
         let areaMedia = parentMedia.clone().removeAll().append(rules)
+
         if (areaParentMedia) {
+          // insert after @media
           areaParentMedia.after(areaMedia)
+        } else if (lastSimilarMedia) {
+          // insert after the closest @media with the same selectors
+          lastSimilarMedia.after(areaMedia)
         } else {
+          // insert after every other Rule
           gridArea.parent.after(areaMedia)
         }
       }


Test patch:
diff --git a/test/autoprefixer.test.js b/test/autoprefixer.test.js
index 81e5507..44432b5 100644
--- a/test/autoprefixer.test.js
+++ b/test/autoprefixer.test.js
@@ -597,6 +597,14 @@ describe('hacks', () => {
     ])
   })
 
+  it('should preserve @media rules with grid-area', () => {
+    let input = read('grid-area-media-sequence')
+    let output = read('grid-area-media-sequence.out')
+    let instance = prefixer('grid-area')
+    let result = postcss([instance]).process(input)
+    expect(result.css).toEqual(output)
+  })
+
   it('ignores values for CSS3PIE props', () => {
     let css = read('pie')
     expect(postcss([compiler]).process(css).css).toEqual(css)
diff --git a/test/cases/grid-area-media-sequence.css b/test/cases/grid-area-media-sequence.css
new file mode 100644
index 0000000..5c2885f
--- /dev/null
+++ b/test/cases/grid-area-media-sequence.css
@@ -0,0 +1,64 @@
+.grid-template-sequence {
+  display: grid;
+  grid-template:
+      "foo foo"
+      "bar bar"
+      "baz baz"
+      / 1fr 1fr;
+}
+
+@media (min-width: 300px) {
+  .grid-template-sequence {
+    grid-template:
+      "foo foo"
+      "bar bar" 20px
+      / 1fr 1fr;
+  }
+}
+
+@media (min-width: 600px) {
+  .grid-template-sequence {
+    grid-template:
+      "foo foo"
+      "bar bar" 20px
+      / 1fr 1fr;
+  }
+}
+
+@media (min-width: 900px) {
+  .grid-template-sequence {
+    grid-template:
+      "foo foo"
+      "foo foo"
+      "bar bar" 20px
+      / 1fr 1fr;
+  }
+}
+
+.foo {
+  grid-area: foo;
+}
+
+.bar {
+  grid-area: bar;
+}
+
+@media (max-width: 900px) {
+  .grid-2 {
+    grid-template:
+      "a a"
+      "a a"
+      "b b" 20px
+      / 1fr 1fr;
+  }
+}
+
+@media (max-width: 400px) {
+  .grid-2 {
+    grid-template:
+      "a a"
+      "a a"
+      "b b" 20px
+      / 1fr 1fr;
+  }
+}
diff --git a/test/cases/grid-area-media-sequence.out.css b/test/cases/grid-area-media-sequence.out.css
new file mode 100644
index 0000000..0c09bd8
--- /dev/null
+++ b/test/cases/grid-area-media-sequence.out.css
@@ -0,0 +1,123 @@
+.grid-template-sequence {
+  display: -ms-grid;
+  display: grid;
+  -ms-grid-rows: auto auto auto;
+  -ms-grid-columns: 1fr 1fr;
+  grid-template:
+      "foo foo"
+      "bar bar"
+      "baz baz"
+      / 1fr 1fr;
+}
+
+@media (min-width: 300px) {
+  .grid-template-sequence {
+    -ms-grid-rows: auto 20px;
+    -ms-grid-columns: 1fr 1fr;
+    grid-template:
+      "foo foo"
+      "bar bar" 20px
+      / 1fr 1fr;
+  }
+}
+
+@media (min-width: 600px) {
+  .grid-template-sequence {
+    -ms-grid-rows: auto 20px;
+    -ms-grid-columns: 1fr 1fr;
+    grid-template:
+      "foo foo"
+      "bar bar" 20px
+      / 1fr 1fr;
+  }
+}
+
+@media (min-width: 900px) {
+  .grid-template-sequence {
+    -ms-grid-rows: auto auto 20px;
+    -ms-grid-columns: 1fr 1fr;
+    grid-template:
+      "foo foo"
+      "foo foo"
+      "bar bar" 20px
+      / 1fr 1fr;
+  }
+}
+
+.foo {
+  -ms-grid-row: 1;
+  -ms-grid-column: 1;
+  -ms-grid-column-span: 2;
+  grid-area: foo;
+}
+
+.bar {
+  -ms-grid-row: 2;
+  -ms-grid-column: 1;
+  -ms-grid-column-span: 2;
+  grid-area: bar;
+}
+
+@media (min-width: 300px) {
+  .foo {
+    -ms-grid-row: 1;
+    -ms-grid-column: 1;
+    -ms-grid-column-span: 2;
+  }
+  .bar {
+    -ms-grid-row: 2;
+    -ms-grid-column: 1;
+    -ms-grid-column-span: 2;
+  }
+}
+
+@media (min-width: 600px) {
+  .foo {
+    -ms-grid-row: 1;
+    -ms-grid-column: 1;
+    -ms-grid-column-span: 2;
+  }
+  .bar {
+    -ms-grid-row: 2;
+    -ms-grid-column: 1;
+    -ms-grid-column-span: 2;
+  }
+}
+
+@media (min-width: 900px) {
+  .foo {
+    -ms-grid-row: 1;
+    -ms-grid-row-span: 2;
+    -ms-grid-column: 1;
+    -ms-grid-column-span: 2;
+  }
+  .bar {
+    -ms-grid-row: 3;
+    -ms-grid-column: 1;
+    -ms-grid-column-span: 2;
+  }
+}
+
+@media (max-width: 900px) {
+  .grid-2 {
+    -ms-grid-rows: auto auto 20px;
+    -ms-grid-columns: 1fr 1fr;
+    grid-template:
+      "a a"
+      "a a"
+      "b b" 20px
+      / 1fr 1fr;
+  }
+}
+
+@media (max-width: 400px) {
+  .grid-2 {
+    -ms-grid-rows: auto auto 20px;
+    -ms-grid-columns: 1fr 1fr;
+    grid-template:
+      "a a"
+      "a a"
+      "b b" 20px
+      / 1fr 1fr;
+  }
+}